home *** CD-ROM | disk | FTP | other *** search
/ Total Network Tools 2002 / NextStepPublishing-TotalNetworkTools2002-Win95.iso / Archive / Misc Servers / Zope.exe / SQLSEM.PY < prev    next >
Encoding:
Python Source  |  1999-11-03  |  94.4 KB  |  2,951 lines

  1.  
  2. """ sql semantics 
  3. """
  4.  
  5. ### trim unused methods.
  6. ### make assns use equivalence classes.
  7.  
  8. ### maybe eventually implement disj-conj-eq optimizations
  9.  
  10. ### note: for multithreading x.relbind(...) should ALWAYs return
  11. ###   a fresh copy of structure (sometimes in-place now).
  12.  
  13. ### note: binding of order by is dubious with archiving,
  14. ###    should not bind IN PLACE, leave unbound elts alone!
  15.  
  16. ### need to fix serialization/deserialization of btand and btor
  17. ###
  18.  
  19. # use kjbuckets builtin if available
  20. try:
  21.     import kjbuckets
  22. except ImportError:
  23.     import kjbuckets0
  24.     kjbuckets = kjbuckets0
  25.     
  26. Tuple = kjbuckets.kjDict
  27. Graph = kjbuckets.kjGraph
  28. Set = kjbuckets.kjSet
  29.  
  30. import sys, traceback
  31. ### debug
  32. #sys.stderr = sys.stdin
  33.     
  34. # operations on simple tuples, mostly from kjbuckets
  35. #def maketuple(thing):
  36. #    """try to make a tuple from thing.
  37. #       thing should be a dictionary or sequence of (name, value)
  38. #       or other tuple."""
  39. #    from types import DictType
  40. #    if type(thing)==DictType:
  41. #       return Tuple(thing.items() )
  42. #    else: return Tuple(thing)
  43.     
  44. def no_ints_nulls(list):
  45.     """in place remove all ints, Nones from a list (for null handling)"""
  46.     tt = type
  47.     nn = None
  48.     from types import IntType
  49.     count = 0
  50.     for x in list:
  51.         if tt(x) is not IntType and x is not nn:
  52.            list[count] = x
  53.            count = count+1
  54.     del list[count:]
  55.     return list
  56.  
  57. # stuff for bound tuples.
  58.  
  59. class HashJoiner:
  60.  
  61.     def __init__(self, bt, relname, attributes, relation, witness):
  62.         self.relname = relname
  63.         self.attributes = attributes
  64.         self.relation = relation
  65.         self.witness = witness 
  66.         self.bt = bt
  67.         eqs = bt.eqs
  68.         #print "relname", relname
  69.         #print "attributes", attributes
  70.         #print "relation", relation
  71.         #print "witness", witness
  72.         #print "bt", bt
  73.         transform = self.transform = kjbuckets.kjDict()
  74.         rbindings = self.rbindings = kjbuckets.kjSet()
  75.         for a in attributes:
  76.             b = (relname, a)
  77.             transform[b] = a
  78.             rbindings[b] = b
  79.         self.eqs = eqs = eqs + kjbuckets.kjGraph(rbindings)
  80.         witness = witness.remap(eqs)
  81.         known = kjbuckets.kjSet(witness.keys()) & rbindings
  82.         batts = tuple(known.items())
  83.         if not batts:
  84.            atts = ()
  85.         elif len(batts)==1:
  86.            atts = ( transform[batts[0]], )
  87.         else:
  88.            atts = transform.dump(batts)
  89.         self.atts = atts
  90.         self.batts = batts
  91.         self.transform = transform
  92.         eqs = bt.eqs
  93.         #eqs = (rbindings * eqs)
  94.         self.eqs = eqs = eqs + kjbuckets.kjGraph(rbindings)
  95.         self.transformG = transformG = eqs * transform
  96.         assns = self.assns = bt.assns
  97.         self.rassns = assns.remap( ~transformG )
  98.         
  99.     def relbind(self, db, atts):
  100.         rel = self.relation
  101.         #print "rel is ", rel, type(rel)
  102.         #print dir(rel)
  103.         if rel.is_view:
  104.             self.relation = rel.relbind(db, atts)
  105.         return self
  106.         
  107.     def uncache(self):
  108.         rel = self.relation
  109.         if rel.is_view:
  110.            self.relation.uncache()
  111.         
  112.     def join(self, subseq):
  113.         relname = self.relname
  114.         result = []
  115.         assns = self.assns
  116.         if not subseq: return result
  117.         # apply equalities to unitary subseq (embedded subq)
  118.         if len(subseq)==1:
  119.            subseq0 = subseq[0]
  120.            subseq0r = subseq0.remap(self.eqs)
  121.            if subseq0r is None:
  122.               return [] # inconsistent
  123.            subseq0 = subseq0 + subseq0r + assns
  124.            if subseq0.Clean() is None:
  125.               return [] # inconsistent
  126.            subseq = [subseq0]
  127.         rassns = self.rassns
  128.         #print "rassns", rassns
  129.         #print "subseq", subseq
  130.         if rassns is None:
  131.            #print "inconsistent btup"
  132.            return []
  133.         relation = self.relation
  134.         #print "assns", assns
  135.         transformG = self.transformG
  136.         #print "transformG", transformG
  137.         transform = self.transform
  138.         atts = self.atts
  139.         batts = self.batts
  140.         #print "batts, atts", batts, atts
  141.         if not batts:
  142.            #print "cross product", relname
  143.            tuples = relation.rows()
  144.            for t in tuples:
  145.                #print "t is", t
  146.                if rassns:
  147.                   t = (t + rassns).Clean()
  148.                if t is None:
  149.                   #print "preselect fails"
  150.                   continue
  151.                new = t.remap(transformG)
  152.                #print "new is", new
  153.                if new is None:
  154.                   #print "transform fails"
  155.                   continue
  156.                for subst in subseq:
  157.                    #print "subst", subst
  158.                    if subst:
  159.                       add = (subst + new).Clean()
  160.                    else:
  161.                       add = new
  162.                    #print "add is", add
  163.                    if add is not None:
  164.                       result.append(add)
  165.         else:
  166.            # hash join
  167.            #print "hash join"
  168.            # first try to use an index
  169.            index = relation.choose_index(atts)
  170.            #print transform
  171.            if index is not None:
  172.               #print "index join", index.name, relname
  173.               #print index.index.keys()
  174.               #print "rassns", rassns
  175.               atts = index.attributes()
  176.               invtransform = ~transform
  177.               if len(atts)==1:
  178.                  batts = (invtransform[atts[0]],)
  179.               else:
  180.                  batts = invtransform.dump(atts)
  181.               hash_tups = 1
  182.               tindex = index.index
  183.               # memoize converted tuples
  184.               tindex0 = {}
  185.               test = tindex.has_key
  186.               test0 = tindex0.has_key
  187.               for i in xrange(len(subseq)):
  188.                   subst = subseq[i]
  189.                   #print "substs is", subst
  190.                   its = subst.dump(batts)
  191.                   #print "its", its
  192.                   othersubsts = []
  193.                   if test0(its):
  194.                      othersubsts = tindex0[its]
  195.                   elif test(its):
  196.                      tups = tindex[its]
  197.                      for t in tups:
  198.                          #print "t before", t
  199.                          t = (t+rassns).Clean()
  200.                          #print "t after", t
  201.                          if t is None: continue
  202.                          new = t.remap(transformG)
  203.                          #print "new", new
  204.                          if new is None: continue
  205.                          othersubsts.append(new)
  206.                   tindex0[its] = othersubsts
  207.                   for other in othersubsts:
  208.                       #print "adding", other, subst
  209.                       add = (other + subst).Clean()
  210.                       if add is not None:
  211.                          result.append(add)
  212.            # hash join
  213.            #print "hash join"
  214.            else:
  215.                tuples = relation.rows()
  216.                if len(subseq)<len(tuples):
  217.                   #print "hash subseq", relname
  218.                   subseqindex = {}
  219.                   test = subseqindex.has_key
  220.                   for i in xrange(len(subseq)):
  221.                       subst = subseq[i]
  222.                       its = subst.dump(batts)
  223.                       #print "items1", subseq, batts, its
  224.                       if test(its):
  225.                          subseqindex[its].append(subst)
  226.                       else:
  227.                          subseqindex[its] = [ subst ]
  228.                   for t in tuples:
  229.                       #print "on", t
  230.                       if rassns:
  231.                          t = (t+rassns).Clean()
  232.                       if t is None:
  233.                          #print "preselect fails"
  234.                          continue
  235.                       its = t.dump(atts)
  236.                       #print "items2", its
  237.                       if test(its):
  238.                          new = t.remap(transformG)
  239.                          #print "...new", new
  240.                          if new is None:
  241.                             #print "transform fails"
  242.                             continue
  243.                          l = subseqindex[its]
  244.                          for subst in l:
  245.                              add = (subst + new).Clean()
  246.                              #print "adding", add
  247.                              if add is not None:
  248.                                 result.append(add)
  249.                else:
  250.                   #print "hash tuples", relname
  251.                   tindex = {}
  252.                   test = tindex.has_key
  253.                   for i in xrange(len(tuples)):
  254.                       t = tuples[i]
  255.                       if rassns:
  256.                          t = (t + rassns).Clean()
  257.                       if t is None:
  258.                          #print "preselect fails"
  259.                          continue
  260.                       new = t.remap(transformG)
  261.                       #print "new is", new
  262.                       if new is None:
  263.                          #print "transform fails"
  264.                          continue
  265.                       its = t.dump(atts)
  266.                       #print "items3", its
  267.                       if test(its):
  268.                          tindex[its].append(new)
  269.                       else:
  270.                          tindex[its] = [ new ]
  271.                   for subst in subseq:
  272.                       its = subst.dump(batts)
  273.                       #print "items4", its
  274.                       if test(its):
  275.                          n = tindex[its]
  276.                          for new in n:
  277.                              add = (subst + new).Clean()
  278.                              if add is not None:
  279.                                 result.append(add)
  280.         #print "hashjoin result"
  281.         #for x in result:
  282.             #print x
  283.             #break
  284.         return result
  285.         
  286. ### essentially, specialized pickle for this app:
  287.         
  288. def deserialize(description):
  289.     """simple protocol for generating a marshallable ob"""
  290.     #print "deserialize", description
  291.     from types import TupleType
  292.     if type(description) is not TupleType:
  293.         return description # base type
  294.     try:
  295.         (name, desc) = description
  296.     except:
  297.         return description # base type
  298.     if name == "tuple":
  299.         # tuple case
  300.         return desc
  301.     ### other special cases here...
  302.     if name == "list":
  303.         # list case: map deserialize across desc
  304.         return map(deserialize, desc)
  305.     # all other cases are classes of sqlsem
  306.     import sqlsem
  307.     klass = getattr(sqlsem, name)
  308.     (args1, args2) = desc
  309.     args1 = tuple(map(deserialize, args1))
  310.     ob = apply(klass, args1)
  311.     ob.demarshal(args2)
  312.     return ob
  313.     
  314. def serialize(ob):
  315.     """dual of deserialize."""
  316.     from types import ListType
  317.     tt = type(ob)
  318.     # for lists map serialize across members
  319.     #if tt is ListType:
  320.     #    return ("list", map(serialize, ob))
  321.     try:
  322.         #print ob.__class__, ob
  323.         args1 = ob.initargs()
  324.         #print "args1 before", args1
  325.         args1 = tuple(map(serialize, args1))
  326.         #print "args1 after", args1
  327.         args2 = ob.marshaldata()
  328.         return (ob.__class__.__name__, (args1, args2))
  329.     except:
  330.         from types import InstanceType
  331.         #tt = type(ob)
  332.         if tt is InstanceType:
  333.            #ext = traceback.extract_tb(sys.exc_traceback)
  334.            #for x in ext: 
  335.                #print x
  336.            #print
  337.            #print sys.exc_type, sys.exc_value
  338.            #print ob.__class__
  339.            raise ValueError, "couldn't serialize %s %s" % (
  340.              tt, ob.__class__)
  341.         # assume base type otherwise
  342.         return ob
  343.         
  344. # invariant:
  345. #   deserialize(serialize(ob)) returns semantic copy of ob
  346. #   serialize(ob) is marshallable
  347. # ie,
  348. #   args1 = ob.initargs() # init args
  349. #   args1d = map(serialize, args1) # serialized
  350. #   args2 = ob.marshaldata() # marshalable addl info
  351. #   # assert args1d, args2 are marshallable
  352. #   args1copy = map(deserialize, args1)
  353. #   ob2 = ob.__class__(args1copy)
  354. #   ob2 = ob2.demarshal(args2)
  355. #   # assert ob2 is semantic copy of ob
  356.  
  357. class SimpleRecursive:
  358.     """Simple Recursive structure, only requires initargs"""
  359.     def demarshal(self, args):
  360.         pass
  361.     def marshaldata(self):
  362.         return ()
  363.  
  364. class BoundTuple:
  365.  
  366.    clean = 1  # false if inconsistent
  367.    closed = 0 # true if equality constraints inferred
  368.  
  369.    def __init__(self, **bindings):
  370.        """bindings are name>simpletuple associations."""
  371.        self.eqs = Graph()
  372.        self.assns = Tuple()
  373.        for (name, simpletuple) in bindings.items():
  374.            self.bind(name, simpletuple)
  375.           
  376.    def initargs(self):
  377.        return ()
  378.        
  379.    def marshaldata(self):
  380.        #print "btp marshaldata", self
  381.        return (self.eqs.items(), self.assns.items(), self.clean, self.closed)
  382.        
  383.    def demarshal(self, args):
  384.        (eitems, aitems, self.clean, self.closed) = args
  385.        self.eqs = kjbuckets.kjGraph(eitems)
  386.        self.assns = kjbuckets.kjDict(aitems)
  387.            
  388.    def relbind(self, dict, db):
  389.        """return bindings of self wrt dict rel>att"""
  390.        result = BoundTuple()
  391.        e2 = result.eqs
  392.        a2 = result.assns
  393.        for ((a,b), (c,d)) in self.eqs.items():
  394.            if a is None:
  395.               try:
  396.                  a = dict[b]
  397.               except KeyError:
  398.                  raise NameError, `b`+": ambiguous or unknown attribute"
  399.            if c is None:
  400.               try:
  401.                  c = dict[d]
  402.               except KeyError:
  403.                  raise NameError, `d`+": ambiguous or unknown attribute"
  404.            e2[(a,b)] = (c,d)
  405.        for ((a,b), v) in self.assns.items():
  406.            if a is None:
  407.               try:
  408.                  a = dict[b]
  409.               except KeyError:
  410.                  raise NameError, `b`+": ambiguous or unknown attribute"
  411.            a2[(a,b)] = v
  412.        result.closed = self.closed
  413.        result.clean = self.clean
  414.        return result
  415.            
  416.    #def known(self, relname):
  417.    #    """return ([(relname, a1), ...], [a1, ...])
  418.    #       for attributes ai of relname known in self."""
  419.    #    atts = []
  420.    #    batts = []
  421.    #    for x in self.assns.keys():
  422.    #        (r,a) = x
  423.    #        if r==relname:
  424.    #           batts.append(x)
  425.    #           atts.append(a)
  426.    #    return (batts, atts)
  427.                  
  428.    def relorder(self, db, allrels):
  429.        """based on known constraints, pick an
  430.           ordering for materializing relations.
  431.           db is database (ignored currently)
  432.           allrels is names of all relations to include (list)."""
  433.        ### not very smart about indices yet!!!
  434.        if len(allrels)<2:
  435.           # doesn't matter
  436.           return allrels
  437.        order = []
  438.        eqs = self.eqs
  439.        assns = self.assns
  440.        kjSet = kjbuckets.kjSet
  441.        kjGraph = kjbuckets.kjGraph
  442.        pinned = kjSet()
  443.        has_index = kjSet()
  444.        needed = kjSet(allrels)
  445.        akeys = assns.keys()
  446.        for (r,a) in akeys:
  447.            pinned[r]=r # pinned if some value known
  448.        known_map = kjGraph(akeys)
  449.        for r in known_map.keys():
  450.            rknown = known_map.neighbors(r)
  451.            if db.has_key(r):
  452.               rel = db[r]
  453.               index = rel.choose_index(rknown)
  454.               if index is not None:
  455.                  has_index[r] = r # has an index!
  456.        if pinned: pinned = pinned & needed
  457.        if has_index: has_index = has_index & needed
  458.        related = kjGraph()
  459.        for ( (r1, a1), (r2, a2) ) in eqs.items():
  460.            related[r1]=r2 # related if equated to other
  461.            related[r2]=r1 # redundant if closed.
  462.        if related: related = needed * related * needed
  463.        chosen = kjSet()
  464.        pr = kjSet(related) & pinned
  465.        # choose first victim
  466.        if has_index:
  467.           choice = has_index.choose_key()
  468.        elif pr:
  469.           choice = pr.choose_key()
  470.        elif pinned:
  471.           choice = pinned.choose_key()
  472.        elif related:
  473.           choice = related.choose_key()
  474.        else:
  475.           return allrels[:] # give up!
  476.        while pinned or related or has_index:
  477.           order.append(choice)
  478.           chosen[choice] = 1
  479.           if pinned.has_key(choice):
  480.              del pinned[choice]
  481.           if related.has_key(choice):
  482.              del related[choice]
  483.           if has_index.has_key(choice):
  484.              del has_index[choice]
  485.           nexts = related * chosen
  486.           if nexts:
  487.              # prefer a relation related to chosen
  488.              choice = nexts.choose_key()
  489.           elif pinned:
  490.              # otherwise one that is pinned
  491.              choice = pinned.choose_key()
  492.           elif related:
  493.              # otherwise one that relates to something...
  494.              choice = related.choose_key()
  495.        others = kjSet(allrels) - chosen
  496.        if others: order = order + others.items()
  497.        return order
  498.            
  499.    def domain(self):
  500.        kjSet = kjbuckets.kjSet
  501.        return kjSet(self.eqs) + kjSet(self.assns)
  502.        
  503.    def __repr__(self):
  504.        from string import join
  505.        result = []
  506.        for ( (name, att), value) in self.assns.items():
  507.            result.append( "%s.%s=%s" % (name, att, `value`) )
  508.        for ( (name, att), (name2, att2) ) in self.eqs.items():
  509.            result.append( "%s.%s=%s.%s" % (name, att, name2, att2) )
  510.        if self.clean:
  511.           if not result: return "TRUE"
  512.        else:
  513.           result.insert(0, "FALSE")
  514.        result.sort()
  515.        return join(result, " & ")
  516.            
  517.    def equate(self, equalities):
  518.        """add equalities to self, only if not closed.
  519.           equalities should be seq of ( (name, att), (name, att) )
  520.           """
  521.        if self.closed: raise ValueError, "cannot add equalities! Closed!"
  522.        e = self.eqs
  523.        for (a, b) in equalities:
  524.            e[a] = b
  525.            
  526.    def close(self):
  527.        """infer equalities, if consistent.
  528.           only recompute equality closure if not previously closed.
  529.           return None on inconsistency.
  530.        """
  531.        neweqs = self.eqs
  532.        if not self.closed:
  533.           self.eqs = neweqs = (neweqs + ~neweqs).tclosure() # sym, trans closure
  534.           self.closed = 1
  535.        # add trivial equalities to self
  536.        for x in self.assns.keys():
  537.            if not neweqs.member(x,x):
  538.               neweqs[x] = x
  539.        newassns = self.assns.remap(neweqs)
  540.        if newassns is not None and self.clean:
  541.           self.assns = newassns
  542.           #self.clean = 1
  543.           return self
  544.        else:
  545.           self.clean = 0
  546.           return None
  547.           
  548.    def share_eqs(self):
  549.        """make clone of self that shares equalities, closure.
  550.           note: will share future side effects to eqs too."""
  551.        result = BoundTuple()
  552.        result.eqs = self.eqs
  553.        result.closed = self.closed
  554.        return result
  555.        
  556.    def __add__(self, other):
  557.        """combine self with other, return closure."""
  558.        result = self.share_eqs()
  559.        se = self.eqs
  560.        oe = other.eqs
  561.        if (se is not oe) and (se != oe):
  562.           result.eqs = se + oe
  563.           result.closed = 0
  564.        ra= result.assns = self.assns + other.assns
  565.        result.clean = result.clean and (ra.Clean() is not None)
  566.        return result.close()
  567.        
  568.    def __and__(self, other):
  569.       """return closed constraints common to self and other."""
  570.       result = BoundTuple()
  571.       se = self.eqs
  572.       oe = other.eqs
  573.       if (se is oe) or (se == oe):
  574.          result.eqs = self.eqs
  575.          result.closed = self.closed
  576.       else:
  577.          result.eqs = self.eqs & other.eqs
  578.       result.assns = self.assns & other.assns
  579.       result.clean = self.clean and other.clean
  580.       return result.close()
  581.       
  582.    def __hash__(self):
  583.       # note: equalities don't enter into hash computation!
  584.       # (some may be spurious)
  585.       self.close()
  586.       return hash(self.assns)# ^ hash(self.eqs)
  587.       
  588.    def __cmp__(self, other):
  589.        test = cmp(self.__class__, other.__class__)
  590.        if test: return test
  591.        sa = self.assns
  592.        oa = other.assns
  593.        test = cmp(sa, oa)
  594.        if test: return test
  595.        kjSet = kjbuckets.kjSet
  596.        kjGraph = kjbuckets.kjSet
  597.        se = self.eqs
  598.        se = kjGraph(se) - kjGraph(kjSet(se))
  599.        oe = other.eqs
  600.        oe = kjGraph(oe) - kjGraph(kjSet(oe))
  601.        return cmp(se, oe)
  602.       
  603. class BoundExpression(SimpleRecursive):
  604.    """superclass for all bound expressions.
  605.       except where overloaded expressions are binary
  606.       with self.left and self.right
  607.    """
  608.    
  609.    contains_aggregate = 0 # default
  610.    
  611.    def __init__(self, left, right):
  612.        self.left = left
  613.        self.right = right
  614.        self.contains_aggregate = left.contains_aggregate or right.contains_aggregate
  615.        
  616.    def initargs(self):
  617.        return (self.left, self.right)
  618.        
  619.    def uncache(self):
  620.        """prepare for execution, clear cached data."""
  621.        self.left.uncache()
  622.        self.right.uncache()
  623.    
  624.    # eventually add converters...
  625.    def equate(self,other):
  626.        """return predicate equating self and other.
  627.           Overload for special cases, please!"""
  628.        return NontrivialEqPred(self, other)
  629.        
  630.    def attribute(self):
  631.        return (None, `self`)
  632.        
  633.    def le(self, other):
  634.        """predicate self<=other"""
  635.        return LessEqPred(self, other)
  636.    
  637.    # these should be overridden for 2 const case someday...
  638.    def lt(self, other):
  639.        """predicate self<other"""
  640.        return LessPred(self, other)
  641.        
  642.    def __coerce__(self, other):
  643.        return (self, other)
  644.        
  645.    def __add__(self, other):
  646.        return BoundAddition(self, other)
  647.        
  648.    def __sub__(self, other):
  649.        return BoundSubtraction(self, other)
  650.        
  651.    def __mul__(self, other):
  652.        return BoundMultiplication(self, other)
  653.        
  654.    def __neg__(self):
  655.        return BoundMinus(self)
  656.        
  657.    def __div__(self, other):
  658.        return BoundDivision(self, other)
  659.        
  660.    def relbind(self, dict, db):
  661.        Class = self.__class__
  662.        return Class(self.left.relbind(dict, db), self.right.relbind(dict, db))
  663.    
  664.    def __repr__(self):
  665.        return "(%s)%s(%s)" % (self.left, self.op, self.right)
  666.        
  667.    def domain(self):
  668.        return self.left.domain() + self.right.domain()
  669.    # always overload value
  670.    
  671. class BoundMinus(BoundExpression, SimpleRecursive):
  672.    def __init__(self, thing):
  673.        self.thing = thing
  674.        self.contains_aggregate = thing.contains_aggregate
  675.    def initargs(self):
  676.        return (self.thing,)
  677.    def __repr__(self):
  678.        return "-(%s)" % (self.thing,)
  679.    def value(self, contexts):
  680.        from types import IntType
  681.        tt = type
  682.        result = self.thing.value(contexts)
  683.        for i in xrange(len(contexts)):
  684.            if tt(contexts[i]) is not IntType:
  685.               result[i] = -result[i]
  686.        return result
  687.    def relbind(self, dict, db):
  688.        Class = self.__class__
  689.        return Class(self.thing.relbind(dict,db))
  690.    def uncache(self):
  691.        self.thing.uncache()
  692.    def domain(self):
  693.        return self.thing.domain()
  694.        
  695.  
  696. ### stuff for aggregation
  697. class Average(BoundMinus):
  698.        
  699.    contains_aggregate = 1
  700.  
  701.    def __init__(self, expr, distinct=0):
  702.        self.distinct = distinct
  703.        if expr.contains_aggregate:
  704.           raise ValueError, `expr`+": aggregate in aggregate "+self.name
  705.        self.thing = expr
  706.        
  707.    name = "Average"
  708.    def __repr__(self):
  709.        distinct = ""
  710.        if self.distinct:
  711.           distinct = "distinct "
  712.        return "%s(%s%s)" % (self.name, distinct, self.thing)
  713.  
  714.    def relbind(self, dict, db):
  715.        Class = self.__class__
  716.        return Class(self.thing.relbind(dict,db), self.distinct)
  717.        
  718.    def value(self, contexts):
  719.        if not contexts: return [] # ???
  720.        test = contexts[0]
  721.        if not test.has_key(None):
  722.           return [self.all_value(contexts)]
  723.        else:
  724.           return self.agg_value(contexts)
  725.           
  726.    def dvalues(self, values):
  727.        d = {}
  728.        for i in xrange(len(values)):
  729.            d[values[i]] = 1
  730.        return d.keys()
  731.           
  732.    def all_value(self, contexts):
  733.        thing = self.thing
  734.        values = self.clean(thing.value(contexts), contexts)
  735.        if self.distinct:
  736.           values = self.dvalues(values)
  737.        return self.op(values)
  738.        
  739.    def clean(self, values, contexts):
  740.        D = {}
  741.        from types import IntType
  742.        tt = type
  743.        for i in xrange(len(contexts)):
  744.            if tt(contexts[i]) is not IntType:
  745.               D[i] = values[i]
  746.        return D.values()
  747.        
  748.    def agg_value(self, contexts):
  749.        from types import IntType
  750.        tt = type
  751.        result = list(contexts)
  752.        for i in xrange(len(contexts)):
  753.            context = contexts[i]
  754.            if tt(context) is not IntType:
  755.               result[i] = self.all_value( context[None] )
  756.        return result
  757.        
  758.    def op(self, values):
  759.        sum = 0
  760.        for x in values:
  761.            sum = sum+x
  762.        return sum/(len(values)*1.0)
  763.        
  764. class Sum(Average):
  765.    name = "Sum"
  766.    def op(self, values):
  767.        if not values: return 0
  768.        sum = values[0]
  769.        for x in values[1:]:
  770.            sum = sum+x
  771.        return sum
  772.        
  773. class Median(Average):
  774.    name = "Median"
  775.    def op(self, values):
  776.        if not values:
  777.           raise ValueError, "Median of empty set"
  778.        values = list(values)
  779.        values.sort()
  780.        lvals = len(values)
  781.        return values[lvals/2]
  782.    
  783. class Maximum(Average):
  784.    name = "Maximum"
  785.    def op(self, values):
  786.        return max(values)
  787.        
  788. class Minimum(Average):
  789.    name = "Minimum"
  790.    def op(self, values):
  791.        return min(values)
  792.        
  793. class Count(Average):
  794.    name = "Count"
  795.    distinct = 0
  796.    def __init__(self, thing, distinct = 0):
  797.        if thing=="*":
  798.           self.thing = "*"
  799.        else:
  800.           Average.__init__(self, thing, distinct)
  801.           
  802.    def domain(self):
  803.        thing = self.thing
  804.        if thing=="*":
  805.           return kjbuckets.kjSet()
  806.        return thing.domain()
  807.        
  808.    def all_value(self, contexts):
  809.        thing = self.thing
  810.        if thing=="*" or not self.distinct:
  811.           test = self.clean(contexts, contexts)
  812.           #print "count len", len(test), contexts[0]
  813.           return len(test)
  814.        return Average.all_value(self, contexts)
  815.    def op(self, values):
  816.        return len(values)
  817.    def relbind(self, dict, db):
  818.        if self.thing=="*":
  819.           return self
  820.        return Average.relbind(self, dict, db)
  821.    def uncache(self):
  822.        if self.thing!="*": self.thing.uncache()
  823.  
  824. def aggregate(assignments, exprlist):
  825.     """aggregates are a assignments with special
  826.        attribute None > list of subtuple"""
  827.     lexprs = len(exprlist)
  828.     if lexprs<1:
  829.        raise ValueError, "aggregate on no expressions?"
  830.     lassns = len(assignments)
  831.     pairs = list(exprlist)
  832.     for i in xrange(lexprs):
  833.         expr = exprlist[i]
  834.         attributes = [expr.attribute()]*lassns
  835.         values = expr.value(assignments)
  836.         pairs[i] = map(None, attributes, values)
  837.     #for x in pairs:
  838.         #print "pairs", x
  839.     if lexprs>1:
  840.        newassnpairs = apply(map, (None,)+tuple(pairs))
  841.     else:
  842.        newassnpairs = pairs[0]
  843.     #for x in newassnpairs:
  844.         #print "newassnpairs", x
  845.     xassns = range(lassns)
  846.     dict = {}
  847.     test = dict.has_key
  848.     for i in xrange(lassns):
  849.         thesepairs = newassnpairs[i]
  850.         thissubassn = assignments[i]
  851.         if test(thesepairs):
  852.            dict[thesepairs].append(thissubassn)
  853.         else:
  854.            dict[thesepairs] = [thissubassn]
  855.     items = dict.items()
  856.     result = list(items)
  857.     kjDict = kjbuckets.kjDict
  858.     if lexprs>1:
  859.        for i in xrange(len(items)):
  860.            (pairs, subassns) = items[i]
  861.            #print "pairs", pairs
  862.            #print "subassns", subassns
  863.            D = kjDict(pairs)
  864.            D[None] = subassns
  865.            result[i] = D
  866.     else:
  867.        for i in xrange(len(items)):
  868.            (pair, subassns) = items[i]
  869.            #print "pair", pair
  870.            #print "subassns", subassns
  871.            result[i] = kjDict( [pair, (None, subassns)] )
  872.     return result
  873.  
  874. ### stuff for order_by
  875. class DescExpr(BoundMinus):
  876.    """special wrapper used only for order by descending
  877.       for things with no -thing operation (eg, strings)"""
  878.       
  879.    def __init__(self, thing):
  880.        self.thing = thing
  881.        self.contains_aggregate = thing.contains_aggregate
  882.  
  883.    def value(self, contexts):
  884.        from types import IntType, StringType
  885.        tt = type
  886.        result = self.thing.value(contexts)
  887.        allwrap = None
  888.        allnowrap = None
  889.        for i in xrange(len(contexts)):
  890.            if tt(contexts[i]) is not IntType:
  891.               resulti = result[i]
  892.               # currently assume only value needing wrapping is string
  893.               if tt(resulti) is StringType:
  894.                  if allnowrap is not None:
  895.                     raise ValueError, "(%s, %s) cannot order desc" % (allnowrap, resulti)
  896.                  allwrap = resulti
  897.                  result[i] = descOb(resulti)
  898.               else:
  899.                  if allwrap is not None:
  900.                     raise ValueError, "(%s, %s) cannot order desc" % (allwrap, resulti)
  901.                  allnowrap = resulti
  902.                  result[i] = -resulti
  903.        return result
  904.    def __repr__(self):
  905.        return "DescExpr(%s)" % (self.thing,)
  906.    def orderbind(self, order):
  907.        """order is list of (att, expr)."""
  908.        Class = self.__class__
  909.        return Class(self.thing.orderbind(order))
  910.        
  911. class SimpleColumn(SimpleRecursive):
  912.    """a simple column name for application to a list of tuples"""
  913.    contains_aggregate = 0
  914.    def __init__(self, name):
  915.        self.name = name
  916.    def relbind(self, dict, db):
  917.        # already bound!
  918.        return self
  919.    def orderbind(self, whatever):
  920.        # already bound!
  921.        return self
  922.    def initargs(self):
  923.        return (self.name,)
  924.    def value(self, simpletuples):
  925.        from types import IntType
  926.        tt = type
  927.        name = self.name
  928.        result = list(simpletuples)
  929.        for i in xrange(len(result)):
  930.            ri = result[i]
  931.            if tt(ri) is not IntType:
  932.               result[i] = ri[name]
  933.            else:
  934.               result[i] = None # ???
  935.        return result
  936.    def __repr__(self):
  937.        return "<SimpleColumn %s>" % (self.name,)
  938.        
  939. class NumberedColumn(BoundMinus):
  940.    """order by column number"""
  941.    contains_aggregate = 0
  942.    def __init__(self, num):
  943.        self.thing = num
  944.    def __repr__(self):
  945.        return "<NumberedColumn %s>" % (self.thing,)
  946.    def relbind(self, dict, db):
  947.        from types import IntType
  948.        if type(self.thing)!=IntType:
  949.           raise ValueError, `self.thing`+": not a numbered column"
  950.        return self
  951.    def orderbind(self, order):
  952.        return SimpleColumn( order[self.thing-1][0] )
  953.        
  954. class OrderExpr(BoundMinus):
  955.    """order by expression."""
  956.    def orderbind(self, order):
  957.        expratt = self.thing.attribute()
  958.        for (att, exp) in order:
  959.            if exp.attribute()==expratt:
  960.               return SimpleColumn(att)
  961.        else:
  962.            raise NameError, `self`+": invalid ordering specification"
  963.    def __repr__(self):
  964.        return "<order expression %s>" % (self.thing,)
  965.        
  966. class descOb:
  967.  
  968.    """special wrapper only used for sorting in descending order
  969.       should only be compared with other descOb instances.
  970.       should only wrap items that cannot be easily "order inverted",
  971.       (eg, strings).
  972.    """
  973.       
  974.    def __init__(self, ob):
  975.        self.ob = ob
  976.        
  977.    def __cmp__(self, other):
  978.        #test = cmp(self.__class__, other.__class__)
  979.        #if test: return test
  980.        return -cmp(self.ob,other.ob)
  981.        
  982.    def __coerce__(self, other):
  983.        return (self, other)
  984.        
  985.    def __hash__(self):
  986.        return hash(self.ob)
  987.        
  988.    def __repr__(self):
  989.        return "descOb(%s)" % (self.ob,)
  990.        
  991. def PositionedSort(num, ord):
  992.     nc = NumberedColumn(num)
  993.     if ord=="DESC":
  994.        return DescExpr(nc)
  995.     return nc
  996.     
  997. def NamedSort(name, ord):
  998.     oe = OrderExpr(name)
  999.     if ord=="DESC":
  1000.        return DescExpr(oe)
  1001.     return oe
  1002.     
  1003. def relbind_sequence(order_list, dict, db):
  1004.     result = list(order_list)
  1005.     for i in xrange(len(order_list)):
  1006.         result[i] = result[i].relbind(dict,db)
  1007.     return result
  1008.         
  1009. def orderbind_sequence(order_list, order):
  1010.     result = list(order_list)
  1011.     for i in xrange(len(order_list)):
  1012.         result[i] = result[i].orderbind(order)
  1013.     return result
  1014.     
  1015. def order_tuples(order_list, tuples):
  1016.     lorder_list = len(order_list)
  1017.     ltuples = len(tuples)
  1018.     if lorder_list<1:
  1019.        raise ValueError, "order on empty list?"
  1020.     order_map = list(order_list)
  1021.     for i in xrange(lorder_list):
  1022.         order_map[i] = order_list[i].value(tuples)
  1023.     if len(order_map)>1:
  1024.        order_vector = apply(map, (None,)+tuple(order_map) )
  1025.     else:
  1026.        order_vector = order_map[0]
  1027.     #G = kjbuckets.kjGraph()
  1028.     pairs = map(None, range(ltuples), tuples)
  1029.     ppairs = map(None, order_vector, pairs)
  1030.     G = kjbuckets.kjGraph(ppairs)
  1031.     #for i in xrange(ltuples):
  1032.     #    G[ order_vector[i] ] = (i, tuples[i])
  1033.     Gkeys = G.keys()
  1034.     Gkeys.sort()
  1035.     result = list(tuples)
  1036.     index = 0
  1037.     for x in Gkeys:
  1038.         #print x
  1039.         for (i, y) in G.neighbors(x):
  1040.             #print "   ", y
  1041.             result[index]=y
  1042.             index = index+1
  1043.     if index!=ltuples:
  1044.        raise ValueError, \
  1045.         "TUPLE LOST IN ORDERING COMPUTATION! (%s,%s)" % (ltuples, index)
  1046.     return result
  1047.    
  1048. class BoundAddition(BoundExpression):
  1049.    """promised addition."""
  1050.    op = "+"
  1051.    def value(self, contexts):
  1052.        from types import IntType
  1053.        tt = type
  1054.        lvs = self.left.value(contexts)
  1055.        rvs = self.right.value(contexts)
  1056.        for i in xrange(len(contexts)):
  1057.            if tt(contexts[i]) is not IntType:
  1058.               lvs[i] = lvs[i] + rvs[i]
  1059.        return lvs
  1060.    
  1061. class BoundSubtraction(BoundExpression):
  1062.    """promised subtraction."""
  1063.    op = "-"
  1064.    def value(self, contexts):
  1065.        from types import IntType
  1066.        tt = type
  1067.        lvs = self.left.value(contexts)
  1068.        rvs = self.right.value(contexts)
  1069.        for i in xrange(len(contexts)):
  1070.            if tt(contexts[i]) is not IntType:
  1071.               lvs[i] = lvs[i] - rvs[i]
  1072.        return lvs
  1073.    
  1074. class BoundMultiplication(BoundExpression):
  1075.    """promised multiplication."""
  1076.    op = "*"
  1077.    def value(self, contexts):
  1078.        from types import IntType
  1079.        tt = type
  1080.        lvs = self.left.value(contexts)
  1081.        rvs = self.right.value(contexts)
  1082.        #print lvs
  1083.        for i in xrange(len(contexts)):
  1084.            if tt(contexts[i]) is not IntType:
  1085.               lvs[i] = lvs[i] * rvs[i]
  1086.        return lvs
  1087.    
  1088. class BoundDivision(BoundExpression):
  1089.    """promised division."""
  1090.    op = "/"
  1091.    def value(self, contexts):
  1092.        from types import IntType
  1093.        tt = type
  1094.        lvs = self.left.value(contexts)
  1095.        rvs = self.right.value(contexts)
  1096.        for i in xrange(len(contexts)):
  1097.            if tt(contexts[i]) is not IntType:
  1098.               lvs[i] = lvs[i] / rvs[i]
  1099.        return lvs
  1100.        
  1101. class BoundAttribute(BoundExpression):
  1102.    """bound attribute: initialize with relname=None if
  1103.       implicit."""
  1104.       
  1105.    contains_aggregate = 0
  1106.       
  1107.    def __init__(self, rel, name):
  1108.        self.rel = rel
  1109.        self.name = name
  1110.        
  1111.    def initargs(self):
  1112.        return (self.rel, self.name)
  1113.        
  1114.    def relbind(self, dict, db):
  1115.        if self.rel is not None: return self
  1116.        name = self.name
  1117.        try:
  1118.            rel = dict[name]
  1119.        except KeyError:
  1120.            raise NameError, `name` + ": unknown or ambiguous"
  1121.        return BoundAttribute(rel, name)
  1122.        
  1123.    def uncache(self):
  1124.        pass
  1125.        
  1126.    def __repr__(self):
  1127.        return "%s.%s" % (self.rel, self.name)
  1128.        
  1129.    def attribute(self):
  1130.        """return (rename, attribute) for self."""
  1131.        return (self.rel, self.name)
  1132.        
  1133.    def domain(self):
  1134.        return kjbuckets.kjSet([ (self.rel, self.name) ])
  1135.        
  1136.    def value(self, contexts):
  1137.        """return value of self in context (bound tuple)."""
  1138.        #print "value of ", self, "in", contexts
  1139.        from types import IntType
  1140.        tt = type
  1141.        result = list(contexts)
  1142.        ra = (self.rel, self.name)
  1143.        for i in xrange(len(result)):
  1144.            if tt(result[i]) is not IntType:
  1145.               result[i] = contexts[i][ra]
  1146.        return result
  1147.        
  1148.    def equate(self, other):
  1149.        oc = other.__class__
  1150.        if oc==BoundAttribute:
  1151.           result = BoundTuple()
  1152.           result.equate([(self.attribute(), other.attribute())])
  1153.           return BTPredicate(result)
  1154.        elif oc==Constant:
  1155.           result = BoundTuple()
  1156.           result.assns[ self.attribute() ] = other.value([1])[0]
  1157.           return BTPredicate(result)
  1158.        else:
  1159.           return NontrivialEqPred(self, other)
  1160.           
  1161. class Constant(BoundExpression):
  1162.    contains_aggregate = 0
  1163.    
  1164.    def __init__(self, value):
  1165.        self.value0 = value
  1166.        
  1167.    def __hash__(self):
  1168.        return hash(self.value0)
  1169.        
  1170.    def initargs(self):
  1171.        return (self.value0,)
  1172.        
  1173.    def domain(self):
  1174.        return kjbuckets.kjSet()
  1175.        
  1176.    def __add__(self, other):
  1177.        if other.__class__==Constant:
  1178.           return Constant(self.value0 + other.value0)
  1179.        return BoundAddition(self, other)
  1180.        
  1181.    def __sub__(self, other):
  1182.        if other.__class__==Constant:
  1183.           return Constant(self.value0 - other.value0)
  1184.        return BoundSubtraction(self, other)
  1185.        
  1186.    def __mul__(self, other):
  1187.        if other.__class__==Constant:
  1188.           return Constant(self.value0 * other.value0)
  1189.        return BoundMultiplication(self, other)
  1190.        
  1191.    def __neg__(self):
  1192.        return Constant(-self.value0)
  1193.        
  1194.    def __div__(self, other):
  1195.        if other.__class__==Constant:
  1196.           return Constant(self.value0 / other.value0)
  1197.        return BoundDivision(self, other)
  1198.  
  1199.    def relbind(self, dict, db):
  1200.        return self
  1201.        
  1202.    def uncache(self):
  1203.        pass
  1204.        
  1205.    def value(self, contexts):
  1206.        """return the constant value associated with self."""
  1207.        return [self.value0] * len(contexts)
  1208.        
  1209.    def equate(self,other):
  1210.        if other.__class__==Constant:
  1211.           if other.value0 == self.value0:
  1212.              return BTPredicate() #true
  1213.           else:
  1214.              return ~BTPredicate() #false
  1215.        else:
  1216.           return other.equate(self)
  1217.           
  1218.    def attribute(self):
  1219.        """invent a pair to identify a constant"""
  1220.        return ('unbound', `self`)
  1221.        
  1222.    def __repr__(self):
  1223.        return "<const %s at %s>" % (`self.value0`, id(self))
  1224.           
  1225. class TupleCollector:
  1226.    """Translate a sequence of assignments to simple tuples.
  1227.       (for implementing the select list of a SELECT).
  1228.    """
  1229.    contains_aggregate = 0
  1230.    contains_nonaggregate = 0
  1231.    
  1232.    def __init__(self):
  1233.        self.final = None
  1234.        self.order = []
  1235.        self.attorder = []
  1236.        self.exporder = []
  1237.        
  1238.    def initargs(self):
  1239.        return ()
  1240.        
  1241.    def marshaldata(self):
  1242.        exps = map(serialize, self.exporder)
  1243.        return (self.attorder, exps, 
  1244.                self.contains_aggregate, self.contains_nonaggregate)
  1245.        
  1246.    def demarshal(self, args):
  1247.        (self.attorder, exps, self.contains_aggregate,
  1248.         self.contains_nonaggregate) = args
  1249.        exporder = self.exporder = map(deserialize, exps)
  1250.        self.order = map(None, self.attorder, exporder)
  1251.        
  1252.    def uncache(self):
  1253.        for exp in self.exporder:
  1254.            exp.uncache()
  1255.        
  1256.    def domain(self):
  1257.        all=[]
  1258.        for e in self.exporder:
  1259.            all = all+e.domain().items()
  1260.        return kjbuckets.kjSet(all)
  1261.        
  1262.    def __repr__(self):
  1263.        l = []
  1264.        for (att, exp) in self.order:
  1265.            l.append( "%s as %s" % (exp, att) )
  1266.        from string import join
  1267.        return join(l, ", ")
  1268.        
  1269.    def addbinding(self, attribute, expression):
  1270.        """bind att>expression."""
  1271.        self.order.append((attribute, expression) )
  1272.        self.attorder.append(attribute )
  1273.        self.exporder.append(expression)
  1274.        if expression.contains_aggregate:
  1275.           self.contains_aggregate = 1
  1276.        else:
  1277.           self.contains_nonaggregate = 1
  1278.        
  1279.    def map(self, assnlist):
  1280.        """remap btlist by self. return (tuplelist, attorder)"""
  1281.        # DON'T eliminate nulls
  1282.        from types import IntType
  1283.        tt = type
  1284.        values = []
  1285.        for exp in self.exporder:
  1286.            values.append(exp.value(assnlist))
  1287.        if len(values)>1:
  1288.           valtups = apply(map, (None,) + tuple(values) )
  1289.        else:
  1290.           valtups = values[0]
  1291.        kjUndump = kjbuckets.kjUndump
  1292.        undumper = tuple(self.attorder)
  1293.        for i in xrange(len(valtups)):
  1294.            test = assnlist[i]
  1295.            if tt(test) is IntType or test is None:
  1296.               valtups[i] = 0 # null/false
  1297.            else:
  1298.               tup = valtups[i]
  1299.               valtups[i] = kjUndump(undumper, tup)
  1300.        return (valtups, self.attorder)
  1301.        
  1302.    def relbind(self, dict, db):
  1303.        """disambiguate missing rel names if possible.
  1304.           also choose output names appropriately."""
  1305.        # CURRENTLY THIS IS AN "IN PLACE" OPERATION
  1306.        order = self.order
  1307.        attorder = self.attorder
  1308.        exporder = self.exporder
  1309.        known = {}
  1310.        for i in xrange(len(order)):
  1311.            (att, exp) = order[i]
  1312.            #print exp
  1313.            exp = exp.relbind(dict, db)
  1314.            if att is None:
  1315.               # choose a name for this column
  1316.               #print exp
  1317.               (rname, aname) = exp.attribute()
  1318.               if known.has_key(aname):
  1319.                  both = rname+"."+aname
  1320.                  att = both
  1321.                  count = 0
  1322.                  while known.has_key(att):
  1323.                     # crank away!
  1324.                     count = count+1
  1325.                     att = both+"."+`count`
  1326.               else:
  1327.                  att = aname
  1328.            else:
  1329.               if known.has_key(att):
  1330.                  raise NameError, `att`+" ambiguous in select list"
  1331.            order[i] = (att, exp)
  1332.            exporder[i] = exp
  1333.            attorder[i] = att
  1334.            known[att] = att
  1335.        return self
  1336.  
  1337. class BTPredicate(SimpleRecursive):
  1338.    """superclass for bound tuple predicates.
  1339.       Eventually should be modified to use "compile" for speed
  1340.       to generate an "inlined" evaluation function.
  1341.       self(bt) returns bt with additional equality constraints
  1342.       (possible) or None if predicate fails."""
  1343.       
  1344.    false = 0
  1345.    constraints = None
  1346.    contains_aggregate = 0
  1347.       
  1348.    def __init__(self, constraints=None):
  1349.        """default interpretation: True."""
  1350.        if constraints is not None:
  1351.           self.constraints = constraints.close()
  1352.           
  1353.    def initargs(self):
  1354.        return (self.constraints,)
  1355.           
  1356.    def relbind(self, dict, db):
  1357.        c = self.constraints
  1358.        if c is None: return self
  1359.        return BTPredicate( self.constraints.relbind(dict, db) )
  1360.        
  1361.    def uncache(self):
  1362.        pass
  1363.           
  1364.    #def evaluate(self, db, relnames):
  1365.        #"""evaluate the predicate over database bindings."""
  1366.        # pretty simple strategy right now...
  1367.        ### need to do something about all/distinct...
  1368.        #c = self.constraints
  1369.        #if c is None:
  1370.        #  c = BoundTuple()
  1371.        #order = c.relorder(db, relnames)
  1372.        #if not order:
  1373.        #   raise ValueError, "attempt to evaluate over no relations: "+`relnames`
  1374.        #result = [c]
  1375.        #for r in order:
  1376.        #    result = hashjoin(result, r, db[r])
  1377.        #if self.__class__==BTPredicate:
  1378.        #   # if it's just equality conjunction, we're done
  1379.        #   return result
  1380.        #else:
  1381.        #   # apply additional constraints
  1382.        #   return self(result)
  1383.           
  1384.    def domain(self):
  1385.        c = self.constraints
  1386.        kjSet = kjbuckets.kjSet
  1387.        if c is None: return kjSet()
  1388.        return c.domain()
  1389.        
  1390.    def __repr__(self):
  1391.        if self.false: return "FALSE"
  1392.        c = self.constraints
  1393.        if c is None: c = "true"
  1394.        return "[pred](%s)" % c
  1395.           
  1396.    def detrivialize(self):
  1397.        """hook added to allow elimination of trivialities
  1398.           return None if completely true, or simpler form
  1399.           or self, if no simplification is possible."""
  1400.        if self.false: return self
  1401.        if not self.constraints: return None
  1402.        return self
  1403.           
  1404.    def negated_constraints(self):
  1405.        """equality constraints always false of satisfactory tuple."""
  1406.        return BoundTuple() # there aren't any
  1407.        
  1408.    def __call__(self, assignments, toplevel=0):
  1409.        """apply self to sequence of assignments
  1410.           return copy of asssignments with false results
  1411.           replaced by 0!  Input may have 0's!"""
  1412.        # optimization
  1413.        #   if toplevel, the btpred has been evaluated during join.
  1414.        if toplevel:
  1415.           return list(assignments)
  1416.        from types import IntType
  1417.        tt = type
  1418.        lbt = len(assignments)
  1419.        if self.false: return [0] * lbt
  1420.        c = self.constraints
  1421.        if c is None or not c:
  1422.           result = assignments[:] # no constraints
  1423.        else:
  1424.           assns = c.assns
  1425.           eqs = c.eqs
  1426.           eqsinteresting = 0
  1427.           for (a,b) in eqs.items():
  1428.               if a!=b:
  1429.                  eqsinteresting = 1
  1430.           result = assignments[:]
  1431.           for i in xrange(lbt):
  1432.               this = assignments[i]
  1433.               #print "comparing", self, "to", this
  1434.               if type(this) is IntType: continue
  1435.               this = (this + assns).Clean()
  1436.               if this is None:
  1437.                  result[i] = 0
  1438.               elif eqsinteresting:
  1439.                  this = this.remap(eqs)
  1440.                  if this is None:
  1441.                     result[i] = 0
  1442.        return result
  1443.           
  1444.    def __and__(self, other):
  1445.        """NOTE: all subclasses must define an __and__!!!"""
  1446.        #print "BTPredicate.__and__", (self, other)
  1447.        if self.__class__==BTPredicate and other.__class__==BTPredicate:
  1448.           c = self.constraints
  1449.           o = other.constraints
  1450.           if c is None: return other
  1451.           if o is None: return self
  1452.           if self.false: return self
  1453.           if other.false: return other
  1454.           # optimization for simple constraints
  1455.           all = (c+o)
  1456.           result = BTPredicate( all ) # all constraints
  1457.           if all is None: result.false = 1
  1458.        else:
  1459.           result = other & self
  1460.        return result
  1461.        
  1462.    def __or__(self, other):
  1463.        if self.__class__==BTPredicate and other.__class__==BTPredicate:
  1464.           c = self.constraints
  1465.           o = other.constraints
  1466.           if c is None: return self # true dominates
  1467.           if o is None: return other
  1468.           if other.false: return self
  1469.           if self.false: return other
  1470.           if self == other: return self
  1471.        result = BTor_pred([self, other])
  1472.        return result
  1473.        
  1474.    def __invert__(self):
  1475.        if self.false:
  1476.           return BTPredicate()
  1477.        if not self.constraints:
  1478.           result = BTPredicate()
  1479.           result.false = 1
  1480.           return result
  1481.        return BTnot_pred(self)
  1482.        
  1483.    def __cmp__(self, other):
  1484.        test = cmp(other.__class__, self.__class__)
  1485.        if test: return test
  1486.        if self.false and other.false: return 0
  1487.        return cmp(self.constraints, other.constraints)
  1488.        
  1489.    def __hash__(self):
  1490.        if self.false: return 11111
  1491.        return hash(self.constraints)
  1492.        
  1493. class BTor_pred(BTPredicate):
  1494.  
  1495.    def __init__(self, members, *othermembers):
  1496.        # replace any OR in members with its members
  1497.        #print "BTor_pred", members
  1498.        members = list(members) + list(othermembers)
  1499.        for m in members[:]:
  1500.            if m.__class__==BTor_pred:
  1501.               members.remove(m)
  1502.               members = members + m.members
  1503.        #print "before", members
  1504.        members = self.members = kjbuckets.kjSet(members).items()
  1505.        #print members
  1506.        for m in members[:]:
  1507.            if m.false: members.remove(m)
  1508.        self.constraints = None # common constraints
  1509.        for m in members:
  1510.           if m.contains_aggregate:
  1511.              self.contains_aggregate = 1
  1512.        if members:
  1513.           # common constraints are those in all members
  1514.           constraints = members[0].constraints
  1515.           for m in members[1:]:
  1516.               mc = m.constraints
  1517.               if not constraints or not mc:
  1518.                  constraints = None
  1519.                  break
  1520.               constraints = constraints & mc
  1521.           self.constraints = constraints
  1522.        #print members
  1523.        
  1524.    def initargs(self):
  1525.        return ((),) + tuple(self.members)
  1526.           
  1527.    def relbind(self, dict, db):
  1528.        ms = []
  1529.        for m in self.members:
  1530.            ms.append( m.relbind(dict, db) )
  1531.        return BTor_pred(ms)
  1532.        
  1533.    def uncache(self):
  1534.        for m in self.members:
  1535.            m.uncache()
  1536.           
  1537.    def domain(self):
  1538.        all = BTPredicate.domain(self).items()
  1539.        for x in self.members:
  1540.            all = all + x.domain().items()
  1541.        return kjbuckets.kjSet(all)
  1542.        
  1543.    def __repr__(self):
  1544.        c = self.constraints
  1545.        m = self.members
  1546.        mr = map(repr, m)
  1547.        from string import join
  1548.        mr.sort()
  1549.        mr = join(mr, " | ")
  1550.        if not mr: mr = "FALSE_OR"
  1551.        if c:
  1552.           mr = "[disj](%s and %s)" % (c, mr)
  1553.        return mr
  1554.  
  1555.    def detrivialize(self):
  1556.        """hook added to allow elimination of trivialities
  1557.           return None if completely true, or simpler form
  1558.           or self, if no simplification is possible."""
  1559.        ms = self.members
  1560.        for i in xrange(len(ms)):
  1561.            ms[i] = ms[i].detrivialize()
  1562.        # now suck out subordinate ors
  1563.        someor = None
  1564.        for m in ms:
  1565.            if m.__class__== BTor_pred:
  1566.               someor = m
  1567.               ms.remove(m)
  1568.               break
  1569.        if someor is not None:
  1570.            result = someor
  1571.            for m in ms:
  1572.                result = result + m
  1573.            return result.detrivialize()
  1574.        allfalse = 1
  1575.        for m in ms:
  1576.            if m is None: allfalse=0; break # true member
  1577.            allfalse = allfalse & m.false
  1578.        if allfalse: return ~BTPredicate() # boundary case
  1579.        ms[:] = filter(None, ms)
  1580.        if not ms: return None # all true.
  1581.        ms[:] = kjbuckets.kjSet(ms).items()
  1582.        if len(ms)==1: return ms[0] # or of 1
  1583.        return self           
  1584.  
  1585.    def __call__(self, boundtuples, toplevel=0):
  1586.        # apply common constraints first
  1587.        lbt = len(boundtuples)
  1588.        # boundary case for or is false
  1589.        members = self.members
  1590.        if not members:
  1591.           return [0] * lbt
  1592.        current = BTPredicate.__call__(self, boundtuples, toplevel)
  1593.        # now apply first alternative
  1594.        alt1 = members[0](current)
  1595.        # determine questionables
  1596.        questionables = current[:]
  1597.        rng = xrange(len(current))
  1598.        from types import IntType
  1599.        tt = type
  1600.        for i in rng:
  1601.            if tt(alt1[i]) is not IntType:
  1602.               questionables[i]=0
  1603.        # now test other alternatives
  1604.        #print "alt1", members[0]
  1605.        #for x in alt1: 
  1606.            #print x
  1607.        for m in self.members[1:]:
  1608.            #print "questionables", m
  1609.            #for x in questionables:
  1610.                #print x
  1611.            passm = m(questionables)
  1612.            for i in rng:
  1613.                test = passm[i]
  1614.                if tt(test) is not IntType:
  1615.                   questionables[i] = 0
  1616.                   alt1[i] = test
  1617.        return alt1
  1618.  
  1619.    def negated_constraints(self):
  1620.        """the negated constraints of an OR are
  1621.           the negated constraints of *all* members"""
  1622.        ms = self.members
  1623.        result = ms.negated_constraints()
  1624.        for m in ms[1:]:
  1625.            if not result: return result
  1626.            mc = m.negated_constraints()
  1627.            if not mc: return mc
  1628.            result = result & mc
  1629.        return result
  1630.        
  1631.    def __and__(self, other):
  1632.        """push "and" down"""
  1633.        newmembers = self.members[:]
  1634.        for i in xrange(len(newmembers)):
  1635.            newmembers[i] = newmembers[i] & other
  1636.        return BTor_pred(newmembers)
  1637.        
  1638.    def __or__(self, other):
  1639.        """collapse two ors, otherwise just add new member"""
  1640.        if self.__class__==BTor_pred and other.__class__==BTor_pred:
  1641.           return BTor_pred(self.members+other.members)
  1642.        return BTor_pred(self.members + [other])
  1643.        
  1644.    def __invert__(self):
  1645.        """translate to and-not"""
  1646.        ms = self.members
  1647.        if not ms: return BTPredicate() # boundary case
  1648.        result = ~ms[0]
  1649.        for m in ms[1:]:
  1650.            result = result & ~m
  1651.        return result
  1652.        
  1653.    def __cmp__(self, other):
  1654.        test = cmp(self.__class__, other.__class__)
  1655.        if test: return test
  1656.        kjSet = kjbuckets.kjSet
  1657.        test = cmp(kjSet(self.members), kjSet(other.members))
  1658.        if test: return test
  1659.        return BTPredicate.__cmp__(self, other)
  1660.        
  1661.    def __hash__(self):
  1662.        return hash(kjbuckets.kjSet(self.members))
  1663.        
  1664. class BTnot_pred(BTPredicate):
  1665.  
  1666.    def __init__(self, thing):
  1667.        self.negated = thing
  1668.        self.contains_aggregate = thing.contains_aggregate
  1669.        self.constraints = thing.negated_constraints()
  1670.        
  1671.    def initargs(self):
  1672.        return (self.negated,)
  1673.        
  1674.    def relbind(self, dict, db):
  1675.        return BTnot_pred( self.negated.relbind(dict, db) )
  1676.        
  1677.    def uncache(self):
  1678.        self.negated.uncache()
  1679.        
  1680.    def domain(self):
  1681.        result = BTPredicate.domain(self) + self.negated.domain()
  1682.        #print "neg domain is", `self`, result
  1683.        return result
  1684.        
  1685.    def __repr__(self):
  1686.        c = self.constraints
  1687.        n = self.negated
  1688.        r = "(NOT %s)" % n
  1689.        if c: r = "[neg](%s & %s)" % (c, r)
  1690.        return r
  1691.  
  1692.    def detrivialize(self):
  1693.        """hook added to allow elimination of trivialities
  1694.           return None if completely true, or simpler form
  1695.           or self, if no simplification is possible."""
  1696.        # first, fix or/and/not precedence
  1697.        thing = self.negated
  1698.        if thing.__class__ == BTnot_pred:
  1699.           return thing.negated.detrivialize()
  1700.        if thing.__class__ == BTor_pred:
  1701.           # translate to and_not
  1702.           members = thing.members[:]
  1703.           for i in xrange(len(members)):
  1704.               members[i] = ~members[i]
  1705.           result = BTand_pred(members)
  1706.           return result.detrivialize()
  1707.        if thing.__class__ == BTand_pred:
  1708.           # translate to or_not
  1709.           members = thing.members[:]
  1710.           c = thing.constraints # precondition
  1711.           if c is not None:
  1712.              members.append(BTPredicate(c))
  1713.           for i in xrange(len(members)):
  1714.               members[i] = ~members[i]
  1715.           result = BTor_pred(members)
  1716.           return result.detrivialize()
  1717.        self.negated = thing = self.negated.detrivialize()
  1718.        if thing is None: return ~BTPredicate() # uniquely false
  1719.        if thing.false: return None # uniquely true
  1720.        return self
  1721.  
  1722.    def __call__(self, boundtuples, toplevel=0):
  1723.        from types import IntType
  1724.        tt = type
  1725.        current = BTPredicate.__call__(self, boundtuples, toplevel)
  1726.        omit = self.negated(current)
  1727.        for i in xrange(len(current)):
  1728.            if tt(omit[i]) is not IntType:
  1729.               current[i]=0
  1730.        return current
  1731.  
  1732.    def negated_constraints(self):
  1733.        """the negated constraints of a NOT are the
  1734.           negated constraints of the thing negated."""
  1735.        return self.negated.constraints
  1736.        
  1737.    def __and__(self, other):
  1738.        """do the obvious thing."""
  1739.        return BTand_pred([self, other])
  1740.        
  1741.    def __or__(self, other):
  1742.        """do the obvious thing"""
  1743.        return BTor_pred([self, other])
  1744.        
  1745.    def __invert__(self):
  1746.        return self.negated
  1747.        
  1748.    def __cmp__(self, other):
  1749.        test = cmp(self.__class__, other.__class__)
  1750.        if test: return test
  1751.        test = cmp(self.negated,other.negated)
  1752.        if test: return test
  1753.        return BTPredicate.__cmp__(self,other)
  1754.        
  1755.    def __hash__(self):
  1756.        return hash(self.negated)^787876^hash(self.constraints)
  1757.        
  1758. class BTand_pred(BTPredicate):
  1759.  
  1760.    def __init__(self, members, precondition=None, *othermembers):
  1761.        #print "BTand_pred", (members, precondition)
  1762.        members = list(members) + list(othermembers)
  1763.        members = self.members = kjbuckets.kjSet(members).items()
  1764.        self.constraints = precondition # common constraints
  1765.        if members:
  1766.           # common constraints are those in any member
  1767.           if precondition is not None:
  1768.              constraints = precondition
  1769.           else:
  1770.              constraints = BoundTuple()
  1771.           for i in xrange(len(members)):
  1772.               m = members[i]
  1773.               mc = m.constraints
  1774.               if mc:
  1775.                  #print "constraints", constraints
  1776.                  constraints = constraints + mc
  1777.                  if constraints is None: break
  1778.               if m.__class__==BTPredicate:
  1779.                  members[i] = None # subsumed above
  1780.           members = self.members = filter(None, members)
  1781.           for m in members:
  1782.               if m.contains_aggregate:
  1783.                  self.contains_aggregate=1
  1784.           ### consider propagating constraints down?
  1785.           self.constraints = constraints
  1786.           if constraints is None: self.false = 1
  1787.           
  1788.    def initargs(self):
  1789.        #print "self.members", self.members
  1790.        #print "self.constraints", self.constraints
  1791.        #return (list(self.members), self.constraints)
  1792.        return ((), self.constraints) + tuple(self.members)
  1793.           
  1794.    def relbind(self, dict, db):
  1795.        ms = []
  1796.        for m in self.members:
  1797.            ms.append( m.relbind(dict, db) )
  1798.        c = self.constraints.relbind(dict, db)
  1799.        return BTand_pred(ms, c)
  1800.        
  1801.    def uncache(self):
  1802.        for m in self.members:
  1803.            m.uncache()
  1804.           
  1805.    def domain(self):
  1806.        all = BTPredicate.domain(self).items()
  1807.        for x in self.members:
  1808.            all = all + x.domain().items()
  1809.        return kjbuckets.kjSet(all)
  1810.        
  1811.    def __repr__(self):
  1812.        m = self.members
  1813.        c = self.constraints
  1814.        r = map(repr, m)
  1815.        if self.false: r.insert(0, "FALSE")
  1816.        from string import join
  1817.        r = join(r, " AND ")
  1818.        r = "(%s)" % r
  1819.        if c: r = "[conj](%s and %s)" % (c, r)
  1820.        return r
  1821.  
  1822.    def detrivialize(self):
  1823.        """hook added to allow elimination of trivialities
  1824.           return None if completely true, or simpler form
  1825.           or self, if no simplification is possible."""
  1826.        # first apply demorgan's law to push ands down
  1827.        # (exponential in worst case).
  1828.        #print "detrivialize"
  1829.        #print self
  1830.        ms = self.members
  1831.        some_or = None
  1832.        c = self.constraints
  1833.        for m in ms:
  1834.            if m.__class__==BTor_pred:
  1835.               some_or = m
  1836.               ms.remove(m)
  1837.               break
  1838.        if some_or is not None:
  1839.           result = some_or
  1840.           if c is not None:
  1841.              some_or = some_or & BTPredicate(c)
  1842.           for m in ms:
  1843.               result = result & m # preserves or/and precedence
  1844.           if result.__class__!=BTor_pred:
  1845.              raise "what the?"
  1846.           result = result.detrivialize()
  1847.           #print "or detected, returning"
  1848.           #print result
  1849.           return result
  1850.        for i in xrange(len(ms)):
  1851.            ms[i] = ms[i].detrivialize()
  1852.        ms[:] = filter(None, ms)
  1853.        if not ms:
  1854.           #print "returning boundary case of condition"
  1855.           if c is None:
  1856.              return None
  1857.           else: 
  1858.              return BTPredicate(c).detrivialize()
  1859.        ms[:] = kjbuckets.kjSet(ms).items()
  1860.        if len(ms)==1 and c is None: 
  1861.           #print "and of 1, returning"
  1862.           #print ms[0]
  1863.           return ms[0] # and of 1
  1864.        return self           
  1865.  
  1866.    def __call__(self, boundtuples, toplevel=0):
  1867.        # apply common constraints first
  1868.        current = BTPredicate.__call__(self, boundtuples, toplevel)
  1869.        for m in self.members:
  1870.            current = m(current)
  1871.        return current
  1872.  
  1873.    def negated_constraints(self):
  1874.        """the negated constraints of an AND are
  1875.           the negated constraints of *any* member"""
  1876.        ms = self.members
  1877.        result = BoundTuple()
  1878.        for m in ms:
  1879.            mc = m.negated_constraints()
  1880.            if mc: result = result + mc 
  1881.        return result
  1882.        
  1883.    def __and__(self, other):
  1884.        """push "and" down if other is an or"""
  1885.        if other.__class__==BTor_pred:
  1886.           return other & self
  1887.        c = self.constraints
  1888.        # merge in other and
  1889.        if other.__class__==BTand_pred:
  1890.           allmem = self.members+other.members
  1891.           oc = other.constraints
  1892.           if c is None:
  1893.              c = oc
  1894.           elif oc is not None:
  1895.              c = c+oc
  1896.           return BTand_pred(allmem, c)
  1897.        return BTand_pred(self.members + [other], c)
  1898.        
  1899.    def __or__(self, other):
  1900.        """do the obvious thing."""
  1901.        return BTor_pred([self, other])
  1902.        
  1903.    def __invert__(self):
  1904.        """translate to or-not"""
  1905.        ms = self.members
  1906.        if not ms: return ~BTPredicate() # boundary case
  1907.        result = ~ms[0]
  1908.        for m in ms[1:]:
  1909.            result = result | ~m
  1910.        return result
  1911.        
  1912.    def __cmp__(self, other):
  1913.        test = cmp(self.__class__, other.__class__)
  1914.        if test: return test
  1915.        kjSet = kjbuckets.kjSet
  1916.        test = cmp(kjSet(self.members), kjSet(other.members))
  1917.        if test: return test
  1918.        return BTPredicate.__cmp__(self, other)
  1919.        
  1920.    def __hash__(self):
  1921.        return hash(kjbuckets.kjSet(self.members))
  1922.        
  1923. class NontrivialEqPred(BTPredicate):
  1924.    """equation of nontrivial expressions."""
  1925.    
  1926.    def __init__(self, left, right):
  1927.        #print "making pred", self.__class__, left, right
  1928.        # maybe should used reflexivity...
  1929.        self.left = left
  1930.        self.right = right
  1931.        self.contains_aggregate = left.contains_aggregate or right.contains_aggregate
  1932.        
  1933.    def initargs(self):
  1934.        return (self.left, self.right)
  1935.        
  1936.    def __cmp__(self, other):
  1937.        test = cmp(self.__class__, other.__class__)
  1938.        if test: return test
  1939.        test = cmp(self.right, other.right)
  1940.        if test: return test
  1941.        return cmp(other.left, other.left)
  1942.        
  1943.    def hash(self, other):
  1944.        return hash(self.left) ^ hash(self.right)
  1945.        
  1946.    def relbind(self, dict, db):
  1947.        Class = self.__class__
  1948.        return Class(self.left.relbind(dict,db), self.right.relbind(dict,db) )
  1949.        
  1950.    def uncache(self):
  1951.        self.left.uncache()
  1952.        self.right.uncache()
  1953.        
  1954.    def domain(self):
  1955.        return self.left.domain() + self.right.domain()
  1956.        
  1957.    op = "=="
  1958.        
  1959.    def __repr__(self):
  1960.        return "(%s)%s(%s)" % (self.left, self.op, self.right)
  1961.        
  1962.    def detrivialize(self):
  1963.        return self
  1964.        
  1965.    def __call__(self, assigns, toplevel=0):
  1966.        from types import IntType
  1967.        tt = type
  1968.        lv = self.left.value(assigns)
  1969.        rv = self.right.value(assigns)
  1970.        result = assigns[:]
  1971.        for i in xrange(len(assigns)):
  1972.            t = assigns[i]
  1973.            if type(t) is not IntType and lv[i]!=rv[i]:
  1974.               result[i] = 0
  1975.        return result
  1976.        
  1977.    def negated_constraints(self):
  1978.        return None
  1979.        
  1980.    def __and__(self, other):
  1981.        return BTand_pred( [self, other] )
  1982.        
  1983.    def __or__(self, other):
  1984.        return BTor_pred( [self, other] )
  1985.        
  1986.    def __invert__(self):
  1987.        return BTnot_pred(self)
  1988.        
  1989. class BetweenPredicate(NontrivialEqPred):
  1990.    """e1 BETWEEN e2 AND e3"""
  1991.    def __init__(self, middle, lower, upper):
  1992.        self.middle = middle
  1993.        self.lower = lower
  1994.        self.upper = upper
  1995.        
  1996.    def initargs(self):
  1997.        return (self.middle, self.lower, self.upper)
  1998.        
  1999.    def domain(self):
  2000.        return (
  2001.    self.middle.domain() + self.lower.domain() + self.upper.domain())
  2002.         
  2003.    def relbind(self, dict, db):
  2004.        self.middle = self.middle.relbind(dict, db)
  2005.        self.lower = self.lower.relbind(dict, db)
  2006.        self.upper = self.upper.relbind(dict, db)
  2007.        return self
  2008.        
  2009.    def uncache(self):
  2010.        self.middle.uncache()
  2011.        self.upper.uncache()
  2012.        self.lower.uncache()
  2013.        
  2014.    def __repr__(self):
  2015.        return "(%s BETWEEN %s AND %s)" % (
  2016.         self.middle, self.lower, self.upper)
  2017.         
  2018.    def __hash__(self):
  2019.        return hash(self.middle)^~hash(self.lower)^hash(self.upper)^55
  2020.        
  2021.    def __cmp__(self, other):
  2022.        test = cmp(self.__class__, other.__class__)
  2023.        if test: return test
  2024.        test = cmp(self.lower, other.lower)
  2025.        if test: return test
  2026.        test = cmp(self.middle, other.middle)
  2027.        if test: return test
  2028.        return cmp(self.upper, other.upper)
  2029.        
  2030.    def __call__(self, assigns, toplevel=0):
  2031.        from types import IntType
  2032.        tt = type
  2033.        lowv = self.lower.value(assigns)
  2034.        upv = self.upper.value(assigns)
  2035.        midv = self.middle.value(assigns)
  2036.        result = assigns[:]
  2037.        for i in xrange(len(assigns)):
  2038.            t = assigns[i]
  2039.            if tt(t) is not IntType:
  2040.               midvi = midv[i]
  2041.               if lowv[i]>midvi or upv[i]<midvi:
  2042.                  result[i] = 0
  2043.        return result
  2044.        
  2045. class ExistsPred(NontrivialEqPred):
  2046.    """EXISTS subquery."""
  2047.    contains_aggregate = 0
  2048.    
  2049.    def __init__(self, subq):
  2050.        self.cached_result = None
  2051.        self.cachable = None
  2052.        self.subq = subq
  2053.        
  2054.    def initargs(self):
  2055.        return (self.subq,)
  2056.        
  2057.    def domain(self):
  2058.        result = self.subq.unbound()
  2059.        # if there are no outer bindings, evaluate ONCE!
  2060.        if not result:
  2061.           self.cachable = 1
  2062.        return result
  2063.        
  2064.    def relbind(self, dict, db):
  2065.        self.subq = self.subq.relbind(db, dict)
  2066.        return self
  2067.        
  2068.    def uncache(self):
  2069.        self.cached_result = None
  2070.        self.subq.uncache()
  2071.        
  2072.    def __repr__(self):
  2073.        return "\nEXISTS\n%s\n" % (self.subq,)
  2074.        
  2075.    def __call__(self, assigns, toplevel=0):
  2076.        ### should optimize!!!
  2077.        #print "exists"
  2078.        #print self.subq
  2079.        from types import IntType
  2080.        tt = type
  2081.        eval = self.subq.eval
  2082.        result = assigns[:]
  2083.        # shortcut: if cachable, eval only once and cache
  2084.        if self.cachable:
  2085.            test = self.cached_result
  2086.            if test is None:
  2087.               self.cached_result = test = eval()
  2088.            #print "exists cached", self.cached_result
  2089.            if test:
  2090.               return result
  2091.            else:
  2092.               return [0] * len(result)
  2093.        kjDict = kjbuckets.kjDict
  2094.        for i in xrange(len(assigns)):
  2095.            #print "exists uncached"
  2096.            assignsi = assigns[i]
  2097.            if tt(assignsi) is IntType: continue
  2098.            testbtup = BoundTuple()
  2099.            testbtup.assns = kjDict(assignsi)
  2100.            test = eval(outerboundtuple=testbtup).rows()
  2101.            #for x in test:
  2102.                #print "exists for", assignsi
  2103.                #print x
  2104.                #break
  2105.            if not test:
  2106.                  result[i] = 0
  2107.        return result
  2108.        
  2109.    def __hash__(self):
  2110.        return hash(self.subq)^3333
  2111.        
  2112.    def __cmp__(self, other):
  2113.        test = cmp(self.__class__, other.__class__)
  2114.        if test: return test
  2115.        return cmp(self.subq, other.subq)
  2116.        
  2117. class QuantEQ(NontrivialEqPred):
  2118.    """Quantified equal any predicate"""
  2119.    
  2120.    def __init__(self, expr, subq):
  2121.        self.expr = expr
  2122.        self.subq = subq
  2123.        self.cachable = 0
  2124.        self.cached_column = None
  2125.        self.att = None
  2126.        
  2127.    def initargs(self):
  2128.        return (self.expr, self.subq)
  2129.        
  2130.    def uncache(self):
  2131.        self.cached_column = None
  2132.        
  2133.    def domain(self):
  2134.        first = self.subq.unbound()
  2135.        if not first:
  2136.           self.cachable = 1
  2137.        more = self.expr.domain()
  2138.        return first + more
  2139.        
  2140.    def relbind(self, dict, db):
  2141.        subq = self.subq = self.subq.relbind(db, dict)
  2142.        self.expr = self.expr.relbind(dict, db)
  2143.        # test that subquery is single column and determine att
  2144.        sl = subq.select_list
  2145.        atts = sl.attorder
  2146.        if len(atts)<>1:
  2147.           raise ValueError, \
  2148.             "Quantified predicate requires unit select list: %s" % atts
  2149.        self.att = atts[0]
  2150.        return self
  2151.        
  2152.    fmt = "(%s %s ANY %s)"
  2153.    op = "="
  2154.        
  2155.    def __repr__(self):
  2156.        return self.fmt % (self.expr, self.op, self.subq)
  2157.        
  2158.    def __call__(self, assigns, toplevel=0):
  2159.        cached_column = self.cached_column
  2160.        cachable = self.cachable
  2161.        expr = self.expr
  2162.        subq = self.subq
  2163.        att = self.att
  2164.        if cachable:
  2165.            if cached_column is None:
  2166.                subqr = subq.eval().rows()
  2167.                cc = self.cached_column = dump_single_column(subqr, att)
  2168.            #print self, "cached", self.cached_column
  2169.        exprvals = expr.value(assigns)
  2170.        kjDict = kjbuckets.kjDict
  2171.        compare = self.compare
  2172.        tt = type
  2173.        from types import IntType
  2174.        result = assigns[:]
  2175.        for i in xrange(len(assigns)):
  2176.            assignsi = assigns[i]
  2177.            if tt(assignsi) is IntType: continue
  2178.            thisval = exprvals[i]
  2179.            testbtup = BoundTuple()
  2180.            testbtup.assns = kjDict(assignsi)
  2181.            if not cachable:
  2182.                subqr = subq.eval(outerboundtuple=testbtup).rows()
  2183.                cc = dump_single_column(subqr, att)
  2184.                #print self, "uncached", cc, thisval
  2185.            if not compare(thisval, cc):
  2186.                #print "eliminated", assignsi
  2187.                result[i] = 0
  2188.        return result
  2189.        
  2190.    def compare(self, value, column):
  2191.        return value in column
  2192.        
  2193.    def __hash__(self):
  2194.        return hash(self.subq) ^ ~hash(self.expr)
  2195.        
  2196.    def __cmp__(self, other):
  2197.        test = cmp(self.__class__, other.__class__)
  2198.        if test: return test
  2199.        test = cmp(self.expr, other.expr)
  2200.        if test: return test
  2201.        return cmp(self.subq, other.subq)
  2202.        
  2203. # "expr IN (subq)" same as "expr = ANY (subq)"
  2204. InPredicate = QuantEQ
  2205.  
  2206. class InLits(NontrivialEqPred):
  2207.    """expr IN literals, support dynamic bindings."""   
  2208.    def __init__(self, expr, lits):
  2209.        self.expr = expr
  2210.        self.lits = lits
  2211.        self.cached_lits = None
  2212.        
  2213.    def initargs(self):
  2214.        return (self.expr, self.lits)
  2215.        
  2216.    def uncache(self):
  2217.        self.cached_lits = None
  2218.        
  2219.    def domain(self):
  2220.        d = []
  2221.        for l in self.lits:
  2222.            d0 = l.domain()
  2223.            if d0:
  2224.                d = d + d0.items()
  2225.        d0 = self.expr.domain()
  2226.        if d:
  2227.           kjSet = kjbuckets.kjSet
  2228.           return d0 + kjSet(d)
  2229.        else:
  2230.           return d0
  2231.        
  2232.    def relbind(self, dict, db):
  2233.        newlits = []
  2234.        for l in self.lits:
  2235.            newlits.append(l.relbind(dict, db))
  2236.        self.lits = newlits
  2237.        self.expr = self.expr.relbind(dict, db)
  2238.        return self
  2239.        
  2240.    fmt = "(%s IN %s)"
  2241.        
  2242.    def __repr__(self):
  2243.        return self.fmt % (self.expr, self.lits)
  2244.        
  2245.    def __call__(self, assigns, toplevel=0):
  2246.        # LITERALS ARE CONSTANT! NEED ONLY LOOK FOR ONE ASSIGN.
  2247.        tt = type
  2248.        from types import IntType
  2249.        litvals = self.cached_lits
  2250.        if litvals is None:
  2251.           assigns0 = []
  2252.           for asn in assigns:
  2253.               if tt(asn) is not IntType:
  2254.                  assigns0.append(asn)
  2255.                  break
  2256.           if not assigns0:
  2257.              # all false/unknown
  2258.              return assigns
  2259.           litvals = []
  2260.           for lit in self.lits:
  2261.               value = lit.value(assigns0)
  2262.               litvals.append(value[0])
  2263.           self.cached_lits = litvals
  2264.        expr = self.expr
  2265.        exprvals = expr.value(assigns)
  2266.        result = assigns[:]
  2267.        for i in xrange(len(assigns)):
  2268.            assignsi = assigns[i]
  2269.            if tt(assignsi) is IntType: continue
  2270.            thisval = exprvals[i]
  2271.            if thisval not in litvals:
  2272.                #print "eliminated", assignsi
  2273.                result[i] = 0
  2274.        return result
  2275.        
  2276.    def compare(self, value, column):
  2277.        return value in column
  2278.        
  2279.    def __hash__(self):
  2280.        return 10 ^ hash(self.expr)
  2281.        
  2282.    def __cmp__(self, other):
  2283.        test = cmp(self.__class__, other.__class__)
  2284.        if test: return test
  2285.        test = cmp(self.expr, other.expr)
  2286.        if test: return test
  2287.        return cmp(self.lits, other.lits)
  2288.  
  2289. class QuantNE(QuantEQ):
  2290.    """Quantified not equal any predicate"""
  2291.    op = "<>"
  2292.    def compare(self, value, column):
  2293.        for x in column:
  2294.            if value!=x: return 1
  2295.        return 0
  2296.        
  2297. ### note: faster NOT IN using QuantNE?
  2298.  
  2299. class QuantLT(QuantEQ):
  2300.    """Quantified less than any predicate"""
  2301.    op = "<"
  2302.    def uncache(self):
  2303.        self.testval = self.cached = self.cached_column = None
  2304.    def compare(self, value, column):
  2305.        if self.cachable:
  2306.            if self.cached:
  2307.                testval = self.testval
  2308.            else:
  2309.                testval = self.testval = max(column)
  2310.                self.cached = 1
  2311.        else:
  2312.            testval = max(column)
  2313.        return value < testval
  2314.  
  2315. class QuantLE(QuantLT):
  2316.    """Quantified less equal any predicate"""
  2317.    op = "<="
  2318.    def compare(self, value, column):
  2319.        if self.cachable:
  2320.            if self.cached:
  2321.                testval = self.testval
  2322.            else:
  2323.                testval = self.testval = max(column)
  2324.                self.cached = 1
  2325.        else:
  2326.            testval = max(column)
  2327.        return value <= testval
  2328.  
  2329. class QuantGE(QuantLT):
  2330.    """Quantified greater equal any predicate"""
  2331.    op = ">="
  2332.    def compare(self, value, column):
  2333.        if self.cachable:
  2334.            if self.cached:
  2335.                testval = self.testval
  2336.            else:
  2337.                testval = self.testval = min(column)
  2338.                self.cached = 1
  2339.        else:
  2340.            testval = min(column)
  2341.        return value >= testval
  2342.        
  2343. class QuantGT(QuantLT):
  2344.    """Quantified greater than any predicate"""
  2345.    op = ">"
  2346.    def compare(self, value, column):
  2347.        if self.cachable:
  2348.            if self.cached:
  2349.                testval = self.testval
  2350.            else:
  2351.                self.testval = testval = min(column)
  2352.                self.cached = 1
  2353.        else:
  2354.            testval = min(column)
  2355.        return value > testval
  2356.        
  2357. def dump_single_column(assigns, att):
  2358.     """dump single column assignment"""
  2359.     result = assigns[:]
  2360.     for i in xrange(len(result)):
  2361.         result[i] = result[i][att]
  2362.     return result
  2363.  
  2364. class LessPred(NontrivialEqPred):
  2365.    op = "<"
  2366.    def __call__(self, assigns, toplevel=0):
  2367.        from types import IntType
  2368.        tt = type
  2369.        lv = self.left.value(assigns)
  2370.        rv = self.right.value(assigns)
  2371.        result = assigns[:]
  2372.        for i in xrange(len(assigns)):
  2373.            t = assigns[i]
  2374.            if tt(t) is not IntType and lv[i]>=rv[i]:
  2375.               result[i] = 0
  2376.        return result
  2377.        
  2378.    def __inv__(self):
  2379.        return LessEqPred(self.right, self.left)
  2380.        
  2381.    def __hash__(self):
  2382.        return hash(self.left)^hash(self.right)
  2383.  
  2384.        
  2385. class LessEqPred(LessPred):
  2386.    op = "<="
  2387.    def __call__(self, assigns, toplevel=0):
  2388.        from types import IntType
  2389.        tt = type
  2390.        lv = self.left.value(assigns)
  2391.        rv = self.right.value(assigns)
  2392.        result = assigns[:]
  2393.        for i in xrange(len(assigns)):
  2394.            t = assigns[i]
  2395.            if tt(t) is not IntType and lv[i]>rv[i]:
  2396.               result[i] = 0
  2397.        return result
  2398.        
  2399.    def __inv__(self):
  2400.        return LessPred(self.right, self.left)
  2401.        
  2402. class SubQueryExpression(BoundMinus, SimpleRecursive):
  2403.    """sub query expression (subq), must eval to single column, single value"""
  2404.    def __init__(self, subq):
  2405.        self.subq = subq
  2406.        self.att = self.cachable = self.cached = self.cached_value = None
  2407.        
  2408.    def initargs(self):
  2409.        return (self.subq,)
  2410.        
  2411.    def uncache(self):
  2412.        self.cached = self.cached_value = None
  2413.  
  2414.    def domain(self):
  2415.        result = self.subq.unbound()
  2416.        if not result:
  2417.            self.cachable = 1
  2418.        #print "expr subq domain", result
  2419.        return result
  2420.        
  2421.    def relbind(self, dict, db):
  2422.        subq = self.subq = self.subq.relbind(db, dict)
  2423.        # test that subquery is single column and determine att
  2424.        sl = subq.select_list
  2425.        atts = sl.attorder
  2426.        if len(atts)<>1:
  2427.           raise ValueError, \
  2428.             "Quantified predicate requires unit select list: %s" % atts
  2429.        self.att = atts[0]
  2430.        return self
  2431.        
  2432.    def __repr__(self):
  2433.        return "(%s)" % self.subq
  2434.        
  2435.    def value(self, contexts):
  2436.        subq = self.subq
  2437.        att = self.att
  2438.        if self.cachable:
  2439.            if self.cached:
  2440.                cached_value = self.cached_value
  2441.            else:
  2442.                self.cached = 1
  2443.                seval = subq.eval().rows()
  2444.                lse = len(seval)
  2445.                if lse<>1:
  2446.                    raise ValueError, \
  2447.           "const subquery expression must return 1 result: got %s" % lse
  2448.                self.cached_value = cached_value = seval[0][att]
  2449.            #print "const subq cached", cached_value
  2450.            return [cached_value] * len(contexts)
  2451.        from types import IntType
  2452.        tt = type
  2453.        result = contexts[:]
  2454.        kjDict = kjbuckets.kjDict
  2455.        for i in xrange(len(contexts)):
  2456.            contextsi = contexts[i]
  2457.            if tt(contextsi) is not IntType:
  2458.                testbtup = BoundTuple()
  2459.                testbtup.assns = kjDict(contextsi)
  2460.                #print "subq exp", testbtup
  2461.                seval = subq.eval(outerboundtuple=testbtup).rows()
  2462.                lse = len(seval)
  2463.                if lse<>1:
  2464.                    raise ValueError, \
  2465.           "dynamic subquery expression must return 1 result: got %s" % lse
  2466.                result[i] = seval[0][att]
  2467.                #print "nonconst subq uncached", result[i], contextsi
  2468.        return result
  2469.  
  2470. SELECT_TEMPLATE = """\
  2471. SELECT %s %s
  2472. FROM %s
  2473. WHERE %s
  2474. GROUP BY %s
  2475. HAVING %s %s
  2476. ORDER BY %s %s
  2477. """
  2478.  
  2479. def dynamic_binding(ndynamic, dynamic):
  2480.     """create bindings from dynamic tuple for ndynamic parameters
  2481.        if a tuple is given create one
  2482.        if a list is given create many
  2483.     """
  2484.     from types import ListType, TupleType
  2485.     if not dynamic:
  2486.        if ndynamic>0:
  2487.              raise ValueError, `ndynamic`+" dynamic parameters unbound"
  2488.        return [kjbuckets.kjDict()]
  2489.     ldyn = len(dynamic)
  2490.     undumper = map(None, [0]*ndynamic, range(ndynamic))
  2491.     undumper = tuple(undumper)
  2492.     tdyn = type(dynamic)
  2493.     if tdyn is TupleType:
  2494.        ldyn = len(dynamic)
  2495.        if len(dynamic)!=ndynamic:
  2496.           raise ValueError, "%s,%s: wrong number of dynamics" % (ldyn,ndynamic)
  2497.        dynamic = [dynamic]
  2498.     elif tdyn is not ListType:
  2499.        raise TypeError, "dynamic parameters must be list or tuple"
  2500.     else:
  2501.        lens = map(len, dynamic)
  2502.        ndynamic = max(lens)
  2503.        if ndynamic!=min(lens):
  2504.           raise ValueError, "dynamic parameters of inconsistent lengths"
  2505.     undumper = map(None, [0]*ndynamic, range(ndynamic))
  2506.     undumper = tuple(undumper)
  2507.     result = list(dynamic)
  2508.     kjUndump = kjbuckets.kjUndump
  2509.     for i in xrange(len(dynamic)):
  2510.         dyn = dynamic[i]
  2511.         ldyn = len(dyn)
  2512.         #print undumper, dyn
  2513.         if ldyn==1:
  2514.            dynresult = kjUndump(undumper, dyn[0])
  2515.         else:
  2516.            dynresult = kjUndump(undumper, dyn)
  2517.         result[i] = dynresult
  2518.     return result
  2519.  
  2520. class Selector:
  2521.    """For implementing, eg the SQL SELECT statement."""
  2522.    def __init__(self, alldistinct,
  2523.                       select_list,
  2524.                       table_reference_list,
  2525.                       where_pred,
  2526.                       group_list,
  2527.                       having_cond,
  2528.                       union_select =None,
  2529.                       order_by_spec =None,
  2530.                       ndynamic=0, # number of dyn params expected
  2531.                       ):
  2532.        self.ndynamic = ndynamic
  2533.        self.alldistinct = alldistinct
  2534.        self.select_list = select_list
  2535.        self.table_list = table_reference_list
  2536.        self.where_pred = where_pred
  2537.        self.group_list = group_list
  2538.        self.having_cond = having_cond
  2539.        self.union_select = union_select
  2540.        self.order_by = order_by_spec
  2541.        #self.union_spec = "DISTINCT" # default union mode
  2542.        self.relbindings = None # binding of relations
  2543.        self.unbound_set = None # unbound attributes
  2544.        self.rel_atts = None # graph of alias>attname bound in self
  2545.        self.all_aggregate = 0
  2546.        if select_list!="*" and not group_list:
  2547.           if select_list.contains_aggregate:
  2548.              ### should restore this check somewhere else!
  2549.              #if select_list.contains_nonaggregate:
  2550.                 #raise ValueError, "aggregates/nonaggregates don't mix without grouping"
  2551.              self.all_aggregate = 1
  2552.        if where_pred and where_pred.contains_aggregate:
  2553.           raise ValueError, "aggregate in WHERE"
  2554.        self.query_plan = None
  2555.           
  2556.    def initargs(self):
  2557.        #print self.alldistinct
  2558.        #print self.select_list
  2559.        #print self.table_list
  2560.        #print self.where_pred
  2561.        #print self.having_cond
  2562.        #print self.union_select
  2563.        #print self.group_list
  2564.        #print self.order_by
  2565.        #print self.ndynamic
  2566.        # note: order by requires special handling
  2567.        return (self.alldistinct, self.select_list, self.table_list, self.where_pred,
  2568.                None, self.having_cond, self.union_select, None,
  2569.                self.ndynamic)
  2570.                
  2571.    def marshaldata(self):
  2572.        order_by = self.order_by
  2573.        if order_by:
  2574.            order_by = map(serialize, order_by)
  2575.        group_list = self.group_list
  2576.        if group_list:
  2577.            group_list = map(serialize, group_list)
  2578.        #print "marshaldata"
  2579.        #print order_by
  2580.        #print group_list
  2581.        return (order_by, group_list)
  2582.        
  2583.    def demarshal(self, data):
  2584.        (order_by, group_list) = data
  2585.        if order_by:
  2586.           order_by = map(deserialize, order_by)
  2587.        if group_list:
  2588.           group_list = map(deserialize, group_list)
  2589.        #print "demarshal"
  2590.        #print order_by
  2591.        #print group_list
  2592.        self.order_by = order_by
  2593.        self.group_list = group_list
  2594.           
  2595.    def unbound(self):
  2596.        result = self.unbound_set
  2597.        if result is None:
  2598.           raise ValueError, "binding not available"
  2599.        return result
  2600.        
  2601.    def uncache(self):
  2602.        wp = self.where_pred
  2603.        hc = self.having_cond
  2604.        sl = self.select_list
  2605.        if wp is not None: wp.uncache()
  2606.        if hc is not None: hc.uncache()
  2607.        sl.uncache()
  2608.        qp = self.query_plan
  2609.        if qp:
  2610.           for joiner in qp:
  2611.               joiner.uncache()
  2612.        
  2613.    def relbind(self, db, outerbindings=None):
  2614.        ad = self.alldistinct
  2615.        sl = self.select_list
  2616.        tl = self.table_list
  2617.        wp = self.where_pred
  2618.        gl = self.group_list
  2619.        hc = self.having_cond
  2620.        us = self.union_select
  2621.        ob = self.order_by
  2622.        test = db.bindings(tl)
  2623.        #print len(test)
  2624.        #for x in test: 
  2625.             #print x
  2626.        (attbindings, relbindings, ambiguous, ambiguousatts) = test
  2627.        if outerbindings:
  2628.           # bind in outerbindings where unambiguous
  2629.           for (a,r) in outerbindings.items():
  2630.               if ((not attbindings.has_key(a))
  2631.                   and (not ambiguousatts.has_key(a)) ):
  2632.                  attbindings[a] = r
  2633.        # fix "*" select list
  2634.        if sl=="*":
  2635.           sl = TupleCollector()
  2636.           for (a,r) in attbindings.items():
  2637.               sl.addbinding(None, BoundAttribute(r,a))
  2638.           for (dotted, (r,a)) in ambiguous.items():
  2639.               sl.addbinding(dotted, BoundAttribute(r,a))
  2640.        sl = sl.relbind(attbindings, db)
  2641.        wp = wp.relbind(attbindings, db)
  2642.        if hc is not None: hc = hc.relbind(attbindings, db)
  2643.        if us is not None: us = us.relbind(db, attbindings)
  2644.        # bind grouping if present
  2645.        if gl:
  2646.           gl = relbind_sequence(gl, attbindings, db)
  2647.        # bind ordering list if present
  2648.        #print ob
  2649.        if ob:
  2650.           ob = relbind_sequence(ob, attbindings, db)
  2651.           ob = orderbind_sequence(ob, sl.order)
  2652.        result = Selector(ad, sl, tl, wp, gl, hc, us, ob)
  2653.        result.relbindings = relbindings
  2654.        result.ndynamic = self.ndynamic
  2655.        result.check_domains()
  2656.        result.plan_query()
  2657.        query_plan = result.query_plan
  2658.        for i in range(len(query_plan)):
  2659.            query_plan[i] = query_plan[i].relbind(db, attbindings)
  2660.        return result
  2661.        
  2662.    def plan_query(self):
  2663.        """generate a query plan (sequence of join operators)."""
  2664.        rel_atts = self.rel_atts # rel>attname
  2665.        where_pred = self.where_pred.detrivialize()
  2666.        #select_list = self.select_list
  2667.        # shortcut
  2668.        if where_pred is None:
  2669.           bt = BoundTuple()
  2670.        else:
  2671.           bt = self.where_pred.constraints
  2672.        if bt is None: 
  2673.           bt = BoundTuple()
  2674.        eqs = kjbuckets.kjGraph(bt.eqs)
  2675.        witness = kjbuckets.kjDict()
  2676.        # set all known and unbound atts as witnessed
  2677.        for att in bt.assns.keys():
  2678.            witness[att] = 1
  2679.        #print self, "self.unbound_set", self.unbound_set
  2680.        for att in self.unbound_set.items():
  2681.            witness[att] = 1
  2682.        relbindings = self.relbindings
  2683.        allrels = relbindings.keys()
  2684.        #print relbindings
  2685.        allrels = bt.relorder(relbindings, allrels)
  2686.        #print allrels
  2687.        rel_atts = self.rel_atts
  2688.        plan = []
  2689.        for rel in allrels:
  2690.            relation = relbindings[rel]
  2691.            ratts = rel_atts.neighbors(rel)
  2692.            h = HashJoiner(bt, rel, ratts, relation, witness)
  2693.            plan.append(h)
  2694.            for a in ratts:
  2695.                ra = (rel, a)
  2696.                witness[ra] = 1
  2697.                eqs[ra] = ra
  2698.            witness = witness.remap(eqs)
  2699.        self.query_plan = plan
  2700.        
  2701.    def check_domains(self):
  2702.        """determine set of unbound names in self.
  2703.        """
  2704.        relbindings = self.relbindings
  2705.        sl = self.select_list
  2706.        wp = self.where_pred
  2707.        gl = self.group_list
  2708.        hc = self.having_cond
  2709.        us = self.union_select
  2710.        all = sl.domain().items()
  2711.        if wp is not None:
  2712.           all = all + wp.domain().items()
  2713.        # ignore group_list ???
  2714.        if hc is not None:
  2715.           all = all + hc.domain().items()
  2716.        kjSet = kjbuckets.kjSet
  2717.        kjGraph = kjbuckets.kjGraph
  2718.        alldomain = kjSet(all)
  2719.        rel_atts = self.rel_atts = kjGraph(all)
  2720.        allnames = kjSet()
  2721.        #print "relbindings", relbindings.keys()
  2722.        for name in relbindings.keys():
  2723.            rel = relbindings[name]
  2724.            for att in rel.attributes():
  2725.                allnames[ (name, att) ] = 1
  2726.        # union compatibility check
  2727.        if us is not None:
  2728.           us.check_domains()
  2729.           myatts = self.attributes()
  2730.           thoseatts = us.attributes()
  2731.           if myatts!=thoseatts:
  2732.              if len(myatts)!=len(thoseatts):
  2733.                 raise IndexError, "outer %s, inner %s: union select lists lengths differ"\
  2734.                   % (len(myatts), len(thoseatts))
  2735.           for p in map(None, myatts, thoseatts):
  2736.               (x,y)=p
  2737.               if x!=y:
  2738.                  raise NameError, "%s union names don't match" % (p,)
  2739.        self.unbound_set = alldomain - allnames
  2740.        
  2741.    def attributes(self):
  2742.        return self.select_list.attorder
  2743.        
  2744.    def eval(self, dynamic=None, outerboundtuple=None):
  2745.        """leaves a lot to be desired.
  2746.           dynamic and outerboundtuple are mutually
  2747.           exclusive.  dynamic is only pertinent to
  2748.           top levels, outerboundtuple to subqueries"""
  2749.        #print "select eval", dynamic, outerboundtuple
  2750.        from gfdb0 import Relation0
  2751.        # only uncache if outerboundtuple is None (not subquery)
  2752.        # ???
  2753.        if outerboundtuple is None:
  2754.            self.uncache()
  2755.        query_plan = self.query_plan
  2756.        where_pred = self.where_pred.detrivialize()
  2757.        select_list = self.select_list
  2758.        # shortcut
  2759.        if where_pred is not None and where_pred.false: 
  2760.           return Relation0(select_list.attorder, [])
  2761.        #print "where_pred", where_pred
  2762.        if where_pred is None or where_pred.constraints is None:
  2763.           assn0 = assn1 = kjbuckets.kjDict()
  2764.        else:
  2765.           assn1 = self.where_pred.constraints.assns
  2766.           assn0 = assn1 = kjbuckets.kjDict(assn1)
  2767.        # erase stored results from possible previous evaluation
  2768.        ndynamic = self.ndynamic
  2769.        if outerboundtuple is not None: 
  2770.           assn1 = assn1 + outerboundtuple.assns
  2771.        elif ndynamic:
  2772.           dyn = dynamic_binding(ndynamic, dynamic)
  2773.           if len(dyn)!=1:
  2774.              raise ValueError, "only one dynamic subst for selection allowed"
  2775.           dyn = dyn[0]
  2776.           assn1 = assn1 + dyn
  2777.           #print "dynamic", bt
  2778.        #print "assn1", assn1
  2779.        # check unbound names
  2780.        unbound_set = self.unbound_set
  2781.        #print "unbound", unbound_set
  2782.        #print unbound_set
  2783.        #print self.rel_atts
  2784.        for pair in unbound_set.items():
  2785.            if not assn1.has_key(pair):
  2786.               raise KeyError, `pair`+": unbound in selection"
  2787.        assn1 = (unbound_set * assn1) + assn0
  2788.        #print "assn1 now", assn1
  2789.        substseq = [assn1]
  2790.        for h in query_plan:
  2791.            #print "***"
  2792.            #for x in substseq:
  2793.                #print x
  2794.            #print "***"
  2795.            substseq = h.join(substseq)
  2796.            if not substseq: break
  2797.            #print "***"
  2798.            #for x in substseq:
  2799.                #print x
  2800.            #print "***"
  2801.        # apply the rest of the where predicate at top level
  2802.        if substseq and where_pred is not None:
  2803.           #where_pred.uncache()
  2804.           substseq = where_pred(substseq, 1)
  2805.        # eliminate zeros/nulls
  2806.        substseq = no_ints_nulls(substseq)
  2807.        # apply grouping if present
  2808.        group_list = self.group_list
  2809.        if substseq and group_list:
  2810.           substseq = aggregate(substseq, group_list)
  2811.           having_cond = self.having_cond
  2812.           #print having_cond
  2813.           if having_cond is not None:
  2814.              #having_cond.uncache()
  2815.              substseq = no_ints_nulls(having_cond(substseq))
  2816.        elif self.all_aggregate:
  2817.           # universal group
  2818.           substseq = [kjbuckets.kjDict( [(None, substseq)] ) ]
  2819.        (tups, attorder) = select_list.map(substseq)
  2820.        # do UNION if present
  2821.        union_select = self.union_select
  2822.        if union_select is not None:
  2823.           tups = union_select.eval(tups, dynamic, outerboundtuple)
  2824.        # apply DISTINCT if appropriate
  2825.        if self.alldistinct=="DISTINCT":
  2826.           tups = kjbuckets.kjSet(tups).items()
  2827.        # apply ordering if present
  2828.        ob = self.order_by
  2829.        if ob:
  2830.           tups = order_tuples(ob, tups)
  2831.        return Relation0(attorder, tups)
  2832.        
  2833.    def __repr__(self):
  2834.        ndyn = ""
  2835.        if self.ndynamic:
  2836.           ndyn = "\n[%s dynamic parameters]" % self.ndynamic
  2837.        result = SELECT_TEMPLATE % (
  2838.          self.alldistinct,
  2839.          self.select_list,
  2840.          self.table_list,
  2841.          self.where_pred,
  2842.          self.group_list,
  2843.          self.having_cond,
  2844.          #union_spec,
  2845.          self.union_select,
  2846.          self.order_by,
  2847.          ndyn
  2848.          )
  2849.        return result   
  2850.        
  2851. class Union(SimpleRecursive):
  2852.    """union clause."""
  2853.  
  2854.    def __init__(self, alldistinct, selection):
  2855.        self.alldistinct = alldistinct
  2856.        self.selection = selection
  2857.        
  2858.    def initargs(self):
  2859.        return (self.alldistinct, self.selection)
  2860.        
  2861.    def unbound(self):
  2862.        return self.selection.unbound()
  2863.        
  2864.    def relbind(self, db, outer=None):
  2865.        self.selection = self.selection.relbind(db, outer)
  2866.        return self
  2867.        
  2868.    def check_domains(self):
  2869.        self.selection.check_domains()
  2870.        
  2871.    def attributes(self):
  2872.        return self.selection.attributes()
  2873.        
  2874.    def eval(self, assns, dyn=None, outer=None):
  2875.        r = self.selection.eval(dyn, outer)
  2876.        rows = r.rows()
  2877.        allrows = rows + assns
  2878.        if self.alldistinct=="DISTINCT":
  2879.           allrows = kjbuckets.kjSet(allrows).items()
  2880.        return allrows
  2881.        
  2882.    def __repr__(self):
  2883.        return "\nUNION %s %s " % (self.alldistinct, self.selection)
  2884.        
  2885. class Intersect(Union):
  2886.    def eval(self, assns, dyn=None, outer=None):
  2887.        r = self.selection.eval(dyn, outer)
  2888.        rows = r.rows()
  2889.        kjSet = kjbuckets.kjSet
  2890.        allrows = (kjSet(assns) & kjSet(rows)).items()
  2891.        return allrows
  2892.    op = "INTERSECT"
  2893.    def __repr__(self):
  2894.        return "\n%s %s" % (self.op, self.selection)
  2895.        
  2896. class Except(Union):
  2897.    def eval(self, assns, dyn=None, outer=None):
  2898.        r = self.selection.eval(dyn, outer)
  2899.        rows = r.rows()
  2900.        kjSet = kjbuckets.kjSet
  2901.        allrows = (kjSet(assns) - kjSet(rows)).items()
  2902.        return allrows
  2903.    op = "EXCEPT"
  2904.        
  2905.               
  2906. class Parse_Context:
  2907.    """contextual information for parsing
  2908.         p.param() returns a new sequence number for external parameter.
  2909.    """
  2910.    # not serializable
  2911.    
  2912.    parameter_index = 0
  2913.    
  2914.    # no __init__ yet
  2915.    def param(self):
  2916.        temp = self.parameter_index
  2917.        self.parameter_index = temp+1
  2918.        return temp
  2919.        
  2920.    def ndynamic(self):
  2921.        return self.parameter_index
  2922. # update/delete/insert statements
  2923. import sqlmod
  2924. CreateTable = sqlmod.CreateTable
  2925. CreateIndex = sqlmod.CreateIndex
  2926. DropIndex = sqlmod.DropIndex
  2927. DropTable = sqlmod.DropTable
  2928. UpdateOp = sqlmod.UpdateOp
  2929. DeleteOp = sqlmod.DeleteOp
  2930. InsertOp = sqlmod.InsertOp
  2931. InsertValues = sqlmod.InsertValues
  2932. InsertSubSelect = sqlmod.InsertSubSelect
  2933. ColumnDef = sqlmod.ColumnDef
  2934. CreateView = sqlmod.CreateView
  2935. DropView = sqlmod.DropView
  2936.  
  2937. # update storage structures from gfdb0
  2938. import gfdb0
  2939. Add_Tuples = gfdb0.Add_Tuples
  2940. Erase_Tuples = gfdb0.Erase_Tuples
  2941. Reset_Tuples = gfdb0.Reset_Tuples
  2942.        
  2943. ####### testing
  2944. # test helpers
  2945. #def tp(**kw):
  2946. #    return maketuple(kw)
  2947.     
  2948. #def st(**kw):
  2949. #    return BTPredicate(BoundTuple(r=kw))
  2950.     
  2951.